Skip to content
Snippets Groups Projects
Verified Commit e7fce459 authored by Louis's avatar Louis :fire:
Browse files

Create panel stats, shop scrolling, swap panels in town menu

parent a47e790c
No related branches found
No related tags found
No related merge requests found
...@@ -7,6 +7,7 @@ mod h_divider; ...@@ -7,6 +7,7 @@ mod h_divider;
mod image_button; mod image_button;
mod inset_icon; mod inset_icon;
mod panel; mod panel;
mod panel_stat;
mod v_divider; mod v_divider;
pub use self::a_text_box::{ pub use self::a_text_box::{
...@@ -24,4 +25,5 @@ pub use self::inset_icon::{ ...@@ -24,4 +25,5 @@ pub use self::inset_icon::{
render_inset_icon_widget, IconContent, InsetIconProps, InsetIconWidget, render_inset_icon_widget, IconContent, InsetIconProps, InsetIconWidget,
}; };
pub use self::panel::{render_panel_widget, PanelProps, PanelVariant, PanelWidget}; pub use self::panel::{render_panel_widget, PanelProps, PanelVariant, PanelWidget};
pub use self::panel_stat::{render_panel_stat_widget, PanelStatProps, PanelStatWidget};
pub use self::v_divider::{render_v_divider, VDividerWidget, VDividerWidgetProps}; pub use self::v_divider::{render_v_divider, VDividerWidget, VDividerWidgetProps};
...@@ -36,6 +36,7 @@ pub fn render_panel_widget( ...@@ -36,6 +36,7 @@ pub fn render_panel_widget(
}; };
*computed = KStyle { *computed = KStyle {
render_command: value(RenderCommand::Quad),
min_height: px(edge_size * 2.0 + 8.0), min_height: px(edge_size * 2.0 + 8.0),
min_width: px(edge_size * 2.0 + 8.0), min_width: px(edge_size * 2.0 + 8.0),
padding: value(Edge::all(Units::Stretch(0.0))), padding: value(Edge::all(Units::Stretch(0.0))),
......
use bevy::prelude::*;
use kayak_ui::prelude::*;
use kayak_ui::widgets::{BackgroundBundle, TextProps, TextWidgetBundle};
use crate::ui::components::*;
use crate::ui::prelude::{px, stretch, value};
use crate::ui::utilities::TextSizer;
use crate::ui::widgets::*;
use crate::{basic_widget, empty_props};
#[derive(Component, Debug, Clone, PartialEq)]
pub struct PanelStatProps {
pub icon: IconContent,
pub text: String,
pub font_size: f32,
pub color: Color,
pub fill: bool,
}
impl Default for PanelStatProps {
fn default() -> Self {
Self {
icon: IconContent::None,
text: String::new(),
font_size: 18.0,
color: Color::BLACK,
fill: false,
}
}
}
impl Widget for PanelStatProps {}
basic_widget!(PanelStatProps => PanelStatWidget);
pub fn render_panel_stat_widget(
In((mut widget_context, entity)): In<(KayakWidgetContext, Entity)>,
mut commands: Commands,
mut query: Query<(&PanelStatProps, &mut ComputedStyles, &KStyle)>,
text_sizer: TextSizer,
) -> bool {
let parent_id = Some(entity);
if let Ok((props, mut computed, style)) = query.get_mut(entity) {
let (text_width, text_height) =
text_sizer.measure_text(props.text.as_str(), props.font_size);
let between = 10.0;
let image_size = props.font_size;
let total_width = text_width + between + image_size;
*computed = if props.fill {
KStyle {
render_command: value(RenderCommand::Quad),
min_width: px(total_width),
width: stretch(1.0),
height: px(text_height),
..Default::default()
}
.with_style(style)
.into()
} else {
KStyle {
render_command: value(RenderCommand::Quad),
width: px(total_width),
height: px(text_height),
..Default::default()
}
.with_style(style)
.into()
};
let wrapper_style = KStyle {
layout_type: value(LayoutType::Row),
col_between: if props.fill {
stretch(1.0)
} else {
px(between)
},
..Default::default()
};
rsx! {
<BackgroundBundle styles={wrapper_style}>
<InsetIconWidget props={InsetIconProps { image: props.icon.clone(), size: image_size }} />
<TextWidgetBundle
text={TextProps {
content: props.text.clone(),
size: props.font_size,
line_height: Some(props.font_size + 2.0),
..Default::default()
}}
styles={KStyle {
color: value(props.color),
..Default::default()
}}
/>
</BackgroundBundle>
};
}
true
}
...@@ -76,7 +76,9 @@ pub fn render_game_panels( ...@@ -76,7 +76,9 @@ pub fn render_game_panels(
}} }}
{ if show_distance_panel { { if show_distance_panel {
constructor! { <StatsPanel /> } constructor! {
<StatsPanel />
}
}} }}
</ElementBundle> </ElementBundle>
}; };
......
use std::marker::PhantomData;
use bevy::asset::Assets;
use bevy::ecs::system::SystemParam;
use bevy::prelude::{ use bevy::prelude::{
Commands, Component, DespawnRecursiveExt, Entity, In, Query, Res, Resource, With, Commands, Component, DespawnRecursiveExt, Entity, In, Query, Res, Resource, With,
}; };
use kayak_font::{Alignment, KayakFont, TextProperties};
use kayak_ui::prelude::{ use kayak_ui::prelude::{
Event, EventDispatcherContext, KayakRootContext, KayakWidgetContext, WidgetParam, WidgetState, Event, EventDispatcherContext, KayakRootContext, KayakWidgetContext, WidgetParam, WidgetState,
}; };
use crate::assets::AssetHandles;
#[derive(bevy::prelude::Component, Clone, PartialEq, Default)] #[derive(bevy::prelude::Component, Clone, PartialEq, Default)]
pub struct EmptyProps; pub struct EmptyProps;
...@@ -214,6 +221,12 @@ pub mod context { ...@@ -214,6 +221,12 @@ pub mod context {
EmptyState, EmptyState,
render_inset_icon_widget render_inset_icon_widget
); );
register_widget!(
widget_context,
PanelStatProps,
EmptyState,
render_panel_stat_widget
);
register_widget_with_resource!( register_widget_with_resource!(
widget_context, widget_context,
TownMenuPanelProps, TownMenuPanelProps,
...@@ -228,6 +241,13 @@ pub mod context { ...@@ -228,6 +241,13 @@ pub mod context {
UITravelInfo, UITravelInfo,
render_transit_panel render_transit_panel
); );
register_widget_with_resource!(
widget_context,
ShopPanelProps,
EmptyState,
UITravelInfo,
render_shop_panel
);
register_widget_with_resource!( register_widget_with_resource!(
widget_context, widget_context,
StatsPanelProps, StatsPanelProps,
...@@ -276,3 +296,33 @@ macro_rules! on_button_click { ...@@ -276,3 +296,33 @@ macro_rules! on_button_click {
) )
}; };
} }
#[derive(SystemParam)]
pub struct TextSizer<'w, 's> {
pub assets: Res<'w, AssetHandles>,
pub fonts: Res<'w, Assets<KayakFont>>,
#[system_param(ignore)]
_phantom: PhantomData<&'s ()>,
}
impl<'w, 's> TextSizer<'w, 's> {
pub fn measure_text(&self, content: &str, font_size: f32) -> (f32, f32) {
let font_data = self
.fonts
.get(&self.assets.kayak_font("equipment_pro"))
.unwrap();
font_data
.measure(
content,
TextProperties {
font_size,
line_height: font_size + 2.0,
alignment: Alignment::Start,
tab_size: 4,
max_size: (100000.0, font_size + 2.0),
},
)
.size()
}
}
...@@ -5,6 +5,7 @@ mod town_menu; ...@@ -5,6 +5,7 @@ mod town_menu;
mod transit_panel; mod transit_panel;
pub use encounter_panel::{render_encounter_panel, EncounterPanel, EncounterPanelProps}; pub use encounter_panel::{render_encounter_panel, EncounterPanel, EncounterPanelProps};
pub use shop_panel::{render_shop_panel, ShopPanel, ShopPanelProps};
pub use stats_panel::{render_stats_panel, StatsPanel, StatsPanelProps}; pub use stats_panel::{render_stats_panel, StatsPanel, StatsPanelProps};
pub use town_menu::{ pub use town_menu::{
render_town_menu_panel, TownMenuPanel, TownMenuPanelProps, TownMenuPanelState, render_town_menu_panel, TownMenuPanel, TownMenuPanelProps, TownMenuPanelState,
......
use bevy::prelude::*; use bevy::prelude::*;
use kayak_ui::prelude::*; use kayak_ui::prelude::*;
use kayak_ui::widgets::{ use kayak_ui::widgets::{
ElementBundle, ScrollBoxBundle, ScrollBoxProps, ScrollContextProviderBundle, TextProps, BackgroundBundle, ElementBundle, ScrollBoxBundle, ScrollBoxProps, ScrollContextProviderBundle,
TextWidgetBundle, TextProps, TextWidgetBundle,
}; };
use crate::assets::AssetHandles;
use crate::states::Player; use crate::states::Player;
use crate::system::utilities::format_ui_distance;
use crate::ui::components::*; use crate::ui::components::*;
use crate::ui::prelude::*; use crate::ui::prelude::*;
use crate::ui::sync::UITravelInfo;
use crate::ui::widgets::*; use crate::ui::widgets::*;
use crate::world::{CurrentResidence, MapQuery, TownPaths}; use crate::world::{CurrentResidence, MapQuery, TownPaths};
use crate::{basic_widget, empty_props, on_button_click}; use crate::{basic_widget, empty_props, on_button_click};
empty_props!(TransitPanelProps); empty_props!(ShopPanelProps);
basic_widget!(TransitPanelProps => TransitPanel); basic_widget!(ShopPanelProps => ShopPanel);
pub fn buysell_button_factory(target: String) -> OnEvent { pub fn buysell_button_factory(target: String) -> OnEvent {
let target = target.clone(); let target = target.clone();
...@@ -56,59 +53,90 @@ pub fn buysell_button_factory(target: String) -> OnEvent { ...@@ -56,59 +53,90 @@ pub fn buysell_button_factory(target: String) -> OnEvent {
) )
} }
pub fn render_transit_panel( pub fn render_shop_panel(
In((widget_context, entity)): In<(KayakWidgetContext, Entity)>, In((widget_context, entity)): In<(KayakWidgetContext, Entity)>,
mut commands: Commands, mut commands: Commands,
ui_data: Res<UITravelInfo>, query: Query<&ShopPanelProps>,
) -> bool { ) -> bool {
let parent_id = Some(entity); let parent_id = Some(entity);
rsx! { let items = vec![
<ElementBundle> ("Armour", 12usize, 12, 14, 2),
<TextWidgetBundle ("Weapons", 1, 13, 14, 2),
text={TextProps { ("Luxury Goods", 13, 27, 14, 2),
content: format!("Set off for:"), ("Ale", 11, 6, 14, 2),
size: 32.0, ("Milk", 2, 5, 14, 2),
..Default::default() ("Cheese", 3, 6, 14, 2),
}} ("Armour", 12usize, 12, 14, 2),
styles={KStyle { ("Weapons", 1, 13, 14, 2),
color: value(Color::BLACK), ("Luxury Goods", 13, 27, 14, 2),
padding: edge_px(20.0), ("Ale", 11, 6, 14, 2),
bottom: px(15.0), ("Milk", 2, 5, 14, 2),
left: stretch(1.0), ("Cheese", 3, 6, 14, 2),
right: stretch(1.0), ("Armour", 12usize, 12, 14, 2),
..Default::default() ("Weapons", 1, 13, 14, 2),
}} ("Luxury Goods", 13, 27, 14, 2),
/> ("Ale", 11, 6, 14, 2),
<ScrollContextProviderBundle> ("Milk", 2, 5, 14, 2),
<ScrollBoxBundle> ("Cheese", 3, 6, 14, 2),
{ ];
for (place, distance) in ui_data.travel_options.iter() {
constructor! { log::info!("{:?}", items);
<ButtonWidget
styles={ if let Ok(props) = query.get(entity) {
KStyle { rsx! {
left: stretch(1.0), <ElementBundle>
right: stretch(1.0), <TextWidgetBundle
width: pct(70.0), text={TextProps {
min_width: px(300.0), content: format!("You find an array of goods to buy:"),
max_width: px(600.0), size: 32.0,
bottom: px(10.0), ..Default::default()
padding_left: px(20.0), }}
padding_right: px(20.0), styles={KStyle {
color: value(Color::BLACK),
padding: edge_px(20.0),
bottom: px(15.0),
left: stretch(1.0),
right: stretch(1.0),
..Default::default()
}}
/>
<ScrollContextProviderBundle>
<ScrollBoxBundle
scroll_box_props={ScrollBoxProps {
track_color: Some(Color::rgb(0.827, 0.482, 0.353)),
thumb_color: Some(Color::rgb(0.451, 0.224, 0.063)),
..Default::default()
} } styles={KStyle { padding: edge_px(5.0), ..Default::default() }}>
{
for (name, idx, cost, shop_amount, player_amount) in items.iter() {
constructor! {
<BackgroundBundle
styles={KStyle {
layout_type: value(LayoutType::Row),
col_between: stretch(0.5),
height: px(40.0),
bottom: px(5.0),
padding_top: px(4.0),
padding_bottom: px(4.0),
color: value(Color::BLACK),
..Default::default() ..Default::default()
} }}
} >
props={ButtonWidgetProps::text(format!("{}: {}", &place, format_ui_distance(*distance)), 28.0)} <InsetIconWidget props={InsetIconProps { image: IconContent::Atlas(String::from("icons"), *idx), size: 32.0}} />
on_event={buysell_button_factory(place.clone())} <TextWidgetBundle styles={KStyle { width: stretch(1.0), ..Default::default() }} text={TextProps { content: String::from(*name), size: 30.0, line_height: Some(32.0), ..Default::default() } }/>
/> <TextWidgetBundle styles={KStyle { width: stretch(1.0), ..Default::default() }} text={TextProps { content: format!("{}g", cost), size: 30.0, line_height: Some(32.0), ..Default::default() } }/>
}; <TextWidgetBundle styles={KStyle { width: stretch(1.0), ..Default::default() }} text={TextProps { content: format!("{}", shop_amount), size: 30.0, line_height: Some(32.0), ..Default::default() } }/>
<TextWidgetBundle styles={KStyle { width: stretch(1.0), ..Default::default() }} text={TextProps { content: format!("{}", player_amount), size: 30.0, line_height: Some(32.0), ..Default::default() } }/>
</BackgroundBundle>
};
}
} }
} </ScrollBoxBundle>
</ScrollBoxBundle> </ScrollContextProviderBundle>
</ScrollContextProviderBundle> </ElementBundle>
</ElementBundle> };
}; }
true true
} }
...@@ -18,13 +18,14 @@ pub fn render_stats_panel( ...@@ -18,13 +18,14 @@ pub fn render_stats_panel(
let parent_id = Some(entity); let parent_id = Some(entity);
let root_style = KStyle { let root_style = KStyle {
position_type: value(KPositionType::SelfDirected), top: px(20.0),
top: px(15.0), left: px(20.0),
left: stretch(1.0),
right: stretch(1.0), right: stretch(1.0),
bottom: stretch(1.0), bottom: stretch(1.0),
height: px(70.0), height: px(160.0),
width: px(160.0),
layout_type: value(LayoutType::Column), layout_type: value(LayoutType::Column),
position_type: value(KPositionType::SelfDirected),
..Default::default() ..Default::default()
}; };
// //
...@@ -40,51 +41,19 @@ pub fn render_stats_panel( ...@@ -40,51 +41,19 @@ pub fn render_stats_panel(
..Default::default() ..Default::default()
}; };
let pad_style = KStyle {
render_command: value(RenderCommand::Quad),
height: px(20.0),
..Default::default()
};
rsx! { rsx! {
<PanelWidget styles={root_style}> <PanelWidget styles={root_style}>
<ElementBundle styles={inner_container_style}> <PanelStatWidget props={PanelStatProps { fill: true, text: distance_travelled, font_size: 24.0, icon: IconContent::Atlas(String::from("icons"), 14), color: Color::BLACK }} />
<InsetIconWidget props={InsetIconProps { size: 28.0, image: IconContent::Atlas(String::from("icons"), 14)}} /> <ElementBundle styles={pad_style.clone()} />
<TextWidgetBundle <PanelStatWidget props={PanelStatProps { fill: true, text: format!("{}g", ui_data.money), font_size: 24.0, icon: IconContent::Atlas(String::from("icons"), 8), color: Color::BLACK }} />
text={TextProps { <ElementBundle styles={pad_style} />
content: distance_travelled, <PanelStatWidget props={PanelStatProps { fill: true, text: format!("{}", ui_data.sustenance), font_size: 24.0, icon: IconContent::Atlas(String::from("icons"), 15), color: Color::BLACK }} />
size: 28.0,
..Default::default()
}}
styles={KStyle {
color: value(Color::BLACK),
bottom: px(6.0),
..Default::default()
}}
/>
<InsetIconWidget props={InsetIconProps { size: 28.0, image: IconContent::Atlas(String::from("icons"), 8)}} />
<TextWidgetBundle
text={TextProps {
content: format!("{}g", ui_data.money),
size: 28.0,
..Default::default()
}}
styles={KStyle {
color: value(Color::BLACK),
bottom: px(6.0),
..Default::default()
}}
/>
<InsetIconWidget props={InsetIconProps { size: 28.0, image: IconContent::Atlas(String::from("icons"), 15)}} />
<TextWidgetBundle
text={TextProps {
content: format!("{}", ui_data.sustenance),
size: 28.0,
..Default::default()
}}
styles={KStyle {
color: value(Color::BLACK),
bottom: px(6.0),
..Default::default()
}}
/>
</ElementBundle>
</PanelWidget> </PanelWidget>
}; };
......
...@@ -23,19 +23,22 @@ pub enum TownMenuTab { ...@@ -23,19 +23,22 @@ pub enum TownMenuTab {
Travel, Travel,
Merchant, Merchant,
Tavern, Tavern,
None,
} }
#[derive(Component, Clone, PartialEq, Default)] #[derive(Component, Clone, PartialEq, Default)]
pub struct TownMenuPanelState { pub struct TownMenuPanelState {
pub tab: TownMenuTab, pub tab: TownMenuTab,
pub next_tab: Option<TownMenuTab>,
} }
pub fn render_town_menu_panel( pub fn render_town_menu_panel(
In((widget_context, entity)): In<(KayakWidgetContext, Entity)>, In((widget_context, entity)): In<(KayakWidgetContext, Entity)>,
mut commands: Commands, mut commands: Commands,
state_query: Query<&TownMenuPanelState>, mut state_query: Query<&mut TownMenuPanelState>,
ui_data: Res<UITravelInfo>, ui_data: Res<UITravelInfo>,
places: Res<TownPaths>, places: Res<TownPaths>,
mut next_state: Local<TownMenuTab>,
) -> bool { ) -> bool {
let parent_id = Some(entity); let parent_id = Some(entity);
let state_entity = let state_entity =
...@@ -71,27 +74,31 @@ pub fn render_town_menu_panel( ...@@ -71,27 +74,31 @@ pub fn render_town_menu_panel(
&mut TownMenuPanelState, &mut TownMenuPanelState,
>| { >| {
if let Ok(mut state) = q.get_mut(state_entity) { if let Ok(mut state) = q.get_mut(state_entity) {
state.tab = TownMenuTab::Travel; state.tab = TownMenuTab::None;
state.next_tab = Some(TownMenuTab::Travel);
} }
}); });
let click_tab_merchant = on_button_click!(Query<&mut TownMenuPanelState>, |mut q: Query< let click_tab_merchant = on_button_click!(Query<&mut TownMenuPanelState>, |mut q: Query<
&mut TownMenuPanelState, &mut TownMenuPanelState,
>| { >| {
if let Ok(mut state) = q.get_mut(state_entity) { if let Ok(mut state) = q.get_mut(state_entity) {
state.tab = TownMenuTab::Merchant; state.tab = TownMenuTab::None;
state.next_tab = Some(TownMenuTab::Merchant);
} }
}); });
let click_tab_tavern = on_button_click!(Query<&mut TownMenuPanelState>, |mut q: Query< let click_tab_tavern = on_button_click!(Query<&mut TownMenuPanelState>, |mut q: Query<
&mut TownMenuPanelState, &mut TownMenuPanelState,
>| { >| {
if let Ok(mut state) = q.get_mut(state_entity) { if let Ok(mut state) = q.get_mut(state_entity) {
state.tab = TownMenuTab::Tavern; state.tab = TownMenuTab::None;
state.next_tab = Some(TownMenuTab::Tavern);
} }
}); });
if let (Some(ref place), Some(state)) = if let (Some(ref place), Some(mut state)) = (
(&ui_data.current_town, state_query.get(state_entity).ok()) &ui_data.current_town,
{ state_query.get_mut(state_entity).ok(),
) {
rsx! { rsx! {
<PanelWidget <PanelWidget
styles={panel_style} styles={panel_style}
...@@ -151,18 +158,30 @@ pub fn render_town_menu_panel( ...@@ -151,18 +158,30 @@ pub fn render_town_menu_panel(
{ {
log::info!("Town menu is showing: {:?}", &state.tab);
match state.tab { match state.tab {
TownMenuTab::Travel => { TownMenuTab::Travel => {
constructor! { constructor! {
<TransitPanel /> <TransitPanel />
}; };
}, },
TownMenuTab::Merchant => {} TownMenuTab::Merchant => {
constructor! {
<ShopPanel />
}
}
TownMenuTab::Tavern => {} TownMenuTab::Tavern => {}
TownMenuTab::None => {}
} }
} }
</PanelWidget> </PanelWidget>
}; };
if let Some(next) = state.next_tab {
log::info!("SWAPPGIN {:?} ;; {:?}", next, state.tab);
state.tab = next;
state.next_tab = None;
}
} }
true true
......
...@@ -60,55 +60,58 @@ pub fn render_transit_panel( ...@@ -60,55 +60,58 @@ pub fn render_transit_panel(
In((widget_context, entity)): In<(KayakWidgetContext, Entity)>, In((widget_context, entity)): In<(KayakWidgetContext, Entity)>,
mut commands: Commands, mut commands: Commands,
ui_data: Res<UITravelInfo>, ui_data: Res<UITravelInfo>,
query: Query<&TransitPanelProps>,
) -> bool { ) -> bool {
let parent_id = Some(entity); let parent_id = Some(entity);
rsx! { if let Ok(props) = query.get(entity) {
<ElementBundle> rsx! {
<TextWidgetBundle <ElementBundle>
text={TextProps { <TextWidgetBundle
content: format!("Set off for:"), text={TextProps {
size: 32.0, content: format!("Set off for:"),
..Default::default() size: 32.0,
}} ..Default::default()
styles={KStyle { }}
color: value(Color::BLACK), styles={KStyle {
padding: edge_px(20.0), color: value(Color::BLACK),
bottom: px(15.0), padding: edge_px(20.0),
left: stretch(1.0), bottom: px(15.0),
right: stretch(1.0), left: stretch(1.0),
..Default::default() right: stretch(1.0),
}} ..Default::default()
/> }}
<ScrollContextProviderBundle> />
<ScrollBoxBundle> <ScrollContextProviderBundle>
{ <ScrollBoxBundle>
for (place, distance) in ui_data.travel_options.iter() { {
constructor! { for (place, distance) in ui_data.travel_options.iter() {
<ButtonWidget constructor! {
styles={ <ButtonWidget
KStyle { styles={
left: stretch(1.0), KStyle {
right: stretch(1.0), left: stretch(1.0),
width: pct(70.0), right: stretch(1.0),
min_width: px(300.0), width: pct(70.0),
max_width: px(600.0), min_width: px(300.0),
bottom: px(10.0), max_width: px(600.0),
padding_left: px(20.0), bottom: px(10.0),
padding_right: px(20.0), padding_left: px(20.0),
..Default::default() padding_right: px(20.0),
..Default::default()
}
} }
} props={ButtonWidgetProps::text(format!("{}: {}", &place, format_ui_distance(*distance)), 28.0)}
props={ButtonWidgetProps::text(format!("{}: {}", &place, format_ui_distance(*distance)), 28.0)} on_event={transit_button_factory(place.clone())}
on_event={transit_button_factory(place.clone())} />
/> };
}; }
} }
} </ScrollBoxBundle>
</ScrollBoxBundle> </ScrollContextProviderBundle>
</ScrollContextProviderBundle> </ElementBundle>
</ElementBundle> };
}; }
true true
} }
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment