Skip to content

Commit

Permalink
Deduplicate ItemEntry
Browse files Browse the repository at this point in the history
  • Loading branch information
vE5li committed May 31, 2024
1 parent dac5aeb commit 57093c2
Show file tree
Hide file tree
Showing 12 changed files with 524 additions and 1,333 deletions.
5 changes: 2 additions & 3 deletions korangar/src/input/event.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
use cgmath::Vector2;
use korangar_interface::event::ClickAction;
use korangar_interface::ElementEvent;
use korangar_networking::{MarketItem, NoMetadata};
use korangar_networking::MarketItem;
use ragnarok_packets::{
AccountId, BuyOrSellOption, CharacterId, CharacterServerInformation, EntityId, HotbarSlot, PurchaseItemInformation, ShopId,
SoldItemInformation, TilePosition,
AccountId, BuyOrSellOption, CharacterId, CharacterServerInformation, EntityId, HotbarSlot, ShopId, SoldItemInformation, TilePosition,
};

use crate::interface::application::{InterfaceSettings, InternalThemeKind};
Expand Down
318 changes: 43 additions & 275 deletions korangar/src/interface/elements/containers/cart.rs
Original file line number Diff line number Diff line change
@@ -1,271 +1,23 @@
use korangar_interface::application::{FontSizeTrait, PositionTraitExt, SizeTraitExt};
use korangar_interface::elements::{
ButtonBuilder, ContainerState, Element, ElementCell, ElementState, ElementWrap, Focus, Headline, WeakElementCell,
ButtonBuilder, ContainerState, Element, ElementCell, ElementState, ElementWrap, Focus, WeakElementCell,
};
use korangar_interface::event::{ChangeEvent, ClickAction, HoverInformation};
use korangar_interface::layout::PlacementResolver;
use korangar_interface::state::{PlainRemote, PlainTrackedState, Remote, TrackedState, TrackedStateExt, TrackedStateVec};
use korangar_interface::state::{PlainRemote, PlainTrackedState, Remote, TrackedState, TrackedStateExt};
use korangar_interface::{dimension_bound, size_bound};
use korangar_networking::{ItemQuantity, MarketItem};
use korangar_networking::MarketItem;
use num::Integer;
use ragnarok_packets::{ItemId, MarketItemInformation};

use crate::graphics::{Color, InterfaceRenderer, Renderer, SpriteRenderer};
use crate::graphics::{Color, InterfaceRenderer, Renderer};
use crate::input::{MouseInputMode, UserEvent};
use crate::interface::application::InterfaceSettings;
use crate::interface::elements::ShopEntry;
use crate::interface::layout::{CornerRadius, ScreenClip, ScreenPosition, ScreenSize};
use crate::interface::resource::{ItemSource, Move, PartialMove};
use crate::interface::theme::InterfaceTheme;
use crate::loaders::{FontSize, ResourceMetadata, Scaling};

pub struct ItemDisplay {
item: MarketItem<(ResourceMetadata, u32)>,
state: ElementState<InterfaceSettings>,
}

impl ItemDisplay {
pub fn new(item: MarketItem<(ResourceMetadata, u32)>) -> Self {
Self {
item,
state: ElementState::default(),
}
}
}

impl Element<InterfaceSettings> for ItemDisplay {
fn get_state(&self) -> &ElementState<InterfaceSettings> {
&self.state
}

fn get_state_mut(&mut self) -> &mut ElementState<InterfaceSettings> {
&mut self.state
}

fn is_focusable(&self) -> bool {
false
}

fn resolve(
&mut self,
placement_resolver: &mut PlacementResolver<InterfaceSettings>,
_application: &InterfaceSettings,
_theme: &InterfaceTheme,
) {
self.state.resolve(placement_resolver, &size_bound!(30, 30));
}

fn render(
&self,
render_target: &mut <InterfaceRenderer as Renderer>::Target,
renderer: &InterfaceRenderer,
application: &InterfaceSettings,
theme: &InterfaceTheme,
parent_position: ScreenPosition,
screen_clip: ScreenClip,
hovered_element: Option<&dyn Element<InterfaceSettings>>,
focused_element: Option<&dyn Element<InterfaceSettings>>,
mouse_mode: &MouseInputMode,
_second_theme: bool,
) {
let mut renderer = self
.state
.element_renderer(render_target, renderer, application, parent_position, screen_clip);

let background_color = match self.is_element_self(hovered_element) || self.is_element_self(focused_element) {
true if matches!(mouse_mode, MouseInputMode::None) => theme.button.hovered_background_color.get(),
_ => theme.button.background_color.get(),
};

renderer.render_background(CornerRadius::uniform(5.0), background_color);

renderer.renderer.render_sprite(
renderer.render_target,
self.item.metadata.0.texture.clone(),
renderer.position,
ScreenSize::uniform(30.0).scaled(Scaling::new(application.get_scaling_factor())),
renderer.clip,
Color::monochrome_u8(255),
false,
);

renderer.render_text(
&format!("{}", self.item.metadata.1),
ScreenPosition::default(),
theme.button.foreground_color.get(),
FontSize::new(12.0),
);
}
}

pub struct CartItemEntry {
state: ContainerState<InterfaceSettings>,
secondary_color: bool,
}

impl CartItemEntry {
fn add_button(
mut cart: PlainTrackedState<Vec<MarketItem<(ResourceMetadata, u32)>>>,
item: MarketItem<(ResourceMetadata, u32)>,
amount: u32,
) -> ElementCell<InterfaceSettings> {
let disabled_cart = cart.clone();

ButtonBuilder::new()
.with_text(format!("- {}", amount))
.with_event(move || {
cart.mutate(|cart| {
let purchase = cart.iter_mut().find(|purchase| purchase.item_id == item.item_id).unwrap();

purchase.metadata.1 = purchase.metadata.1.saturating_sub(amount);

if purchase.metadata.1 == 0 {
cart.retain(|purchase| purchase.item_id != item.item_id);
}
});

vec![]
})
.with_disabled_selector(move || {
disabled_cart
.get()
.iter()
.find(|cart_item| cart_item.item_id == item.item_id)
.map(|cart_item| amount.saturating_sub(cart_item.metadata.1) == 0)
.unwrap_or(true)
})
.with_width_bound(dimension_bound!(20%))
.build()
.wrap()
}

pub fn new(
item: MarketItem<(ResourceMetadata, u32)>,
mut cart: PlainTrackedState<Vec<MarketItem<(ResourceMetadata, u32)>>>,
secondary_color: bool,
) -> Self {
let elements = vec![
ItemDisplay::new(item.clone()).wrap(),
Headline::new(format!("{}: {}", item.metadata.0.name, item.metadata.1), size_bound!(!, 16)).wrap(),
Self::add_button(cart.clone(), item.clone(), 1),
Self::add_button(cart.clone(), item.clone(), 10),
Self::add_button(cart.clone(), item.clone(), 100),
Self::add_button(cart.clone(), item.clone(), 1000),
ButtonBuilder::new()
.with_text("- all")
.with_event(move || {
cart.mutate(|cart| {
cart.retain(|purchase| purchase.item_id != item.item_id);
});

vec![]
})
.with_width_bound(dimension_bound!(20%))
.build()
.wrap(),
];

let state = ContainerState::new(elements);

Self { state, secondary_color }
}
}

impl Element<InterfaceSettings> for CartItemEntry {
fn get_state(&self) -> &ElementState<InterfaceSettings> {
&self.state.state
}

fn get_state_mut(&mut self) -> &mut ElementState<InterfaceSettings> {
&mut self.state.state
}

fn link_back(&mut self, weak_self: WeakElementCell<InterfaceSettings>, weak_parent: Option<WeakElementCell<InterfaceSettings>>) {
self.state.link_back(weak_self, weak_parent);
}

fn is_focusable(&self) -> bool {
self.state.is_focusable::<false>()
}

fn focus_next(
&self,
self_cell: ElementCell<InterfaceSettings>,
caller_cell: Option<ElementCell<InterfaceSettings>>,
focus: Focus,
) -> Option<ElementCell<InterfaceSettings>> {
self.state.focus_next::<false>(self_cell, caller_cell, focus)
}

fn restore_focus(&self, self_cell: ElementCell<InterfaceSettings>) -> Option<ElementCell<InterfaceSettings>> {
self.state.restore_focus(self_cell)
}

fn resolve(
&mut self,
placement_resolver: &mut PlacementResolver<InterfaceSettings>,
application: &InterfaceSettings,
theme: &InterfaceTheme,
) {
let size_bound = &size_bound!(100%, ?);
self.state
.resolve(placement_resolver, application, theme, size_bound, ScreenSize::uniform(8.0));
}

fn hovered_element(&self, mouse_position: ScreenPosition, mouse_mode: &MouseInputMode) -> HoverInformation<InterfaceSettings> {
match mouse_mode {
MouseInputMode::MoveItem(..) => self.state.state.hovered_element(mouse_position),
MouseInputMode::None => self.state.hovered_element(mouse_position, mouse_mode, false),
_ => HoverInformation::Missed,
}
}

// fn drop_resource(&mut self, drop_resource: PartialMove) -> Option<Move> {
// let PartialMove::Item { source, item } = drop_resource else {
// return None;
// };
//
// (source != ItemSource::Inventory).then_some(Move::Item {
// source,
// destination: ItemSource::Inventory,
// item,
// })
// }

fn render(
&self,
render_target: &mut <InterfaceRenderer as Renderer>::Target,
renderer: &InterfaceRenderer,
application: &InterfaceSettings,
theme: &InterfaceTheme,
parent_position: ScreenPosition,
screen_clip: ScreenClip,
hovered_element: Option<&dyn Element<InterfaceSettings>>,
focused_element: Option<&dyn Element<InterfaceSettings>>,
mouse_mode: &MouseInputMode,
second_theme: bool,
) {
let mut renderer = self
.state
.state
.element_renderer(render_target, renderer, application, parent_position, screen_clip);

match self.secondary_color {
true => renderer.render_background(CornerRadius::uniform(5.0), Color::monochrome_u8(60)),
false => renderer.render_background(CornerRadius::uniform(5.0), Color::monochrome_u8(50)),
}

self.state.render(
&mut renderer,
application,
theme,
hovered_element,
focused_element,
mouse_mode,
second_theme,
);
}
}
use crate::loaders::{FontSize, ResourceMetadata};

// TODO: Make this generic too
pub struct CartSum {
total_price: u32,
state: ElementState<InterfaceSettings>,
Expand Down Expand Up @@ -296,8 +48,8 @@ impl Element<InterfaceSettings> for CartSum {
fn resolve(
&mut self,
placement_resolver: &mut PlacementResolver<InterfaceSettings>,
application: &InterfaceSettings,
theme: &InterfaceTheme,
_application: &InterfaceSettings,
_theme: &InterfaceTheme,
) {
self.state.resolve(placement_resolver, &size_bound!(100%, 20));
}
Expand All @@ -307,13 +59,13 @@ impl Element<InterfaceSettings> for CartSum {
render_target: &mut <InterfaceRenderer as Renderer>::Target,
renderer: &InterfaceRenderer,
application: &InterfaceSettings,
theme: &InterfaceTheme,
_theme: &InterfaceTheme,
parent_position: ScreenPosition,
screen_clip: ScreenClip,
hovered_element: Option<&dyn Element<InterfaceSettings>>,
focused_element: Option<&dyn Element<InterfaceSettings>>,
mouse_mode: &MouseInputMode,
second_theme: bool,
_hovered_element: Option<&dyn Element<InterfaceSettings>>,
_focused_element: Option<&dyn Element<InterfaceSettings>>,
_mouse_mode: &MouseInputMode,
_second_theme: bool,
) {
let mut renderer = self
.state
Expand All @@ -340,7 +92,35 @@ impl CartContainer {
.get()
.iter()
.enumerate()
.map(|(index, item)| CartItemEntry::new(item.clone(), cart.clone(), index.is_odd()))
.map(|(index, item)| {
ShopEntry::new(
item.clone(),
cart.clone(),
"-",
|item| item.metadata.0.texture.clone(),
|item| item.metadata.0.name.clone(),
|item| Some(item.metadata.1 as usize),
|item, cart, amount| {
cart.mutate(|cart| {
let purchase = cart.iter_mut().find(|purchase| purchase.item_id == item.item_id).unwrap();

purchase.metadata.1 = purchase.metadata.1.saturating_sub(amount);

if purchase.metadata.1 == 0 {
cart.retain(|purchase| purchase.item_id != item.item_id);
}
});
},
|item, cart, amount| {
cart.get()
.iter()
.find(|cart_item| cart_item.item_id == item.item_id)
.map(|cart_item| amount.saturating_sub(cart_item.metadata.1) == 0)
.unwrap_or(true)
},
index.is_odd(),
)
})
.map(ElementWrap::wrap)
.collect::<Vec<ElementCell<InterfaceSettings>>>();

Expand Down Expand Up @@ -453,18 +233,6 @@ impl Element<InterfaceSettings> for CartContainer {
}
}

fn drop_resource(&mut self, drop_resource: PartialMove) -> Option<Move> {
let PartialMove::Item { source, item } = drop_resource else {
return None;
};

(source != ItemSource::Inventory).then_some(Move::Item {
source,
destination: ItemSource::Inventory,
item,
})
}

fn render(
&self,
render_target: &mut <InterfaceRenderer as Renderer>::Target,
Expand Down
Loading

0 comments on commit 57093c2

Please sign in to comment.