From e7fce4596536bee0147799c931fe8f1edd055f2d Mon Sep 17 00:00:00 2001
From: Louis Capitanchik <contact@louiscap.co>
Date: Thu, 8 Dec 2022 16:34:39 +0000
Subject: [PATCH] Create panel stats, shop scrolling, swap panels in town menu

---
 game_core/src/ui/components/mod.rs        |   2 +
 game_core/src/ui/components/panel.rs      |   1 +
 game_core/src/ui/components/panel_stat.rs | 104 +++++++++++++++++
 game_core/src/ui/screens/in_game.rs       |   4 +-
 game_core/src/ui/utilities.rs             |  50 ++++++++
 game_core/src/ui/widgets/mod.rs           |   1 +
 game_core/src/ui/widgets/shop_panel.rs    | 134 +++++++++++++---------
 game_core/src/ui/widgets/stats_panel.rs   |  63 +++-------
 game_core/src/ui/widgets/town_menu.rs     |  35 ++++--
 game_core/src/ui/widgets/transit_panel.rs |  91 ++++++++-------
 10 files changed, 332 insertions(+), 153 deletions(-)
 create mode 100644 game_core/src/ui/components/panel_stat.rs

diff --git a/game_core/src/ui/components/mod.rs b/game_core/src/ui/components/mod.rs
index 31e028e..1a21bf9 100644
--- a/game_core/src/ui/components/mod.rs
+++ b/game_core/src/ui/components/mod.rs
@@ -7,6 +7,7 @@ mod h_divider;
 mod image_button;
 mod inset_icon;
 mod panel;
+mod panel_stat;
 mod v_divider;
 
 pub use self::a_text_box::{
@@ -24,4 +25,5 @@ pub use self::inset_icon::{
 	render_inset_icon_widget, IconContent, InsetIconProps, InsetIconWidget,
 };
 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};
diff --git a/game_core/src/ui/components/panel.rs b/game_core/src/ui/components/panel.rs
index ac1f19e..87d70b5 100644
--- a/game_core/src/ui/components/panel.rs
+++ b/game_core/src/ui/components/panel.rs
@@ -36,6 +36,7 @@ pub fn render_panel_widget(
 		};
 
 		*computed = KStyle {
+			render_command: value(RenderCommand::Quad),
 			min_height: px(edge_size * 2.0 + 8.0),
 			min_width: px(edge_size * 2.0 + 8.0),
 			padding: value(Edge::all(Units::Stretch(0.0))),
diff --git a/game_core/src/ui/components/panel_stat.rs b/game_core/src/ui/components/panel_stat.rs
new file mode 100644
index 0000000..8764d43
--- /dev/null
+++ b/game_core/src/ui/components/panel_stat.rs
@@ -0,0 +1,104 @@
+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
+}
diff --git a/game_core/src/ui/screens/in_game.rs b/game_core/src/ui/screens/in_game.rs
index e152050..94e051a 100644
--- a/game_core/src/ui/screens/in_game.rs
+++ b/game_core/src/ui/screens/in_game.rs
@@ -76,7 +76,9 @@ pub fn render_game_panels(
 			}}
 
 			{ if show_distance_panel {
-				constructor! { <StatsPanel /> }
+				constructor! {
+					<StatsPanel />
+				}
 			}}
 		</ElementBundle>
 	};
diff --git a/game_core/src/ui/utilities.rs b/game_core/src/ui/utilities.rs
index 8a4e3fb..f8607ff 100644
--- a/game_core/src/ui/utilities.rs
+++ b/game_core/src/ui/utilities.rs
@@ -1,10 +1,17 @@
+use std::marker::PhantomData;
+
+use bevy::asset::Assets;
+use bevy::ecs::system::SystemParam;
 use bevy::prelude::{
 	Commands, Component, DespawnRecursiveExt, Entity, In, Query, Res, Resource, With,
 };
+use kayak_font::{Alignment, KayakFont, TextProperties};
 use kayak_ui::prelude::{
 	Event, EventDispatcherContext, KayakRootContext, KayakWidgetContext, WidgetParam, WidgetState,
 };
 
+use crate::assets::AssetHandles;
+
 #[derive(bevy::prelude::Component, Clone, PartialEq, Default)]
 pub struct EmptyProps;
 
@@ -214,6 +221,12 @@ pub mod context {
 				EmptyState,
 				render_inset_icon_widget
 			);
+			register_widget!(
+				widget_context,
+				PanelStatProps,
+				EmptyState,
+				render_panel_stat_widget
+			);
 			register_widget_with_resource!(
 				widget_context,
 				TownMenuPanelProps,
@@ -228,6 +241,13 @@ pub mod context {
 				UITravelInfo,
 				render_transit_panel
 			);
+			register_widget_with_resource!(
+				widget_context,
+				ShopPanelProps,
+				EmptyState,
+				UITravelInfo,
+				render_shop_panel
+			);
 			register_widget_with_resource!(
 				widget_context,
 				StatsPanelProps,
@@ -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()
+	}
+}
diff --git a/game_core/src/ui/widgets/mod.rs b/game_core/src/ui/widgets/mod.rs
index c706e56..8c6515b 100644
--- a/game_core/src/ui/widgets/mod.rs
+++ b/game_core/src/ui/widgets/mod.rs
@@ -5,6 +5,7 @@ mod town_menu;
 mod transit_panel;
 
 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 town_menu::{
 	render_town_menu_panel, TownMenuPanel, TownMenuPanelProps, TownMenuPanelState,
diff --git a/game_core/src/ui/widgets/shop_panel.rs b/game_core/src/ui/widgets/shop_panel.rs
index b008d44..f3d95d1 100644
--- a/game_core/src/ui/widgets/shop_panel.rs
+++ b/game_core/src/ui/widgets/shop_panel.rs
@@ -1,22 +1,19 @@
 use bevy::prelude::*;
 use kayak_ui::prelude::*;
 use kayak_ui::widgets::{
-	ElementBundle, ScrollBoxBundle, ScrollBoxProps, ScrollContextProviderBundle, TextProps,
-	TextWidgetBundle,
+	BackgroundBundle, ElementBundle, ScrollBoxBundle, ScrollBoxProps, ScrollContextProviderBundle,
+	TextProps, TextWidgetBundle,
 };
 
-use crate::assets::AssetHandles;
 use crate::states::Player;
-use crate::system::utilities::format_ui_distance;
 use crate::ui::components::*;
 use crate::ui::prelude::*;
-use crate::ui::sync::UITravelInfo;
 use crate::ui::widgets::*;
 use crate::world::{CurrentResidence, MapQuery, TownPaths};
 use crate::{basic_widget, empty_props, on_button_click};
 
-empty_props!(TransitPanelProps);
-basic_widget!(TransitPanelProps => TransitPanel);
+empty_props!(ShopPanelProps);
+basic_widget!(ShopPanelProps => ShopPanel);
 
 pub fn buysell_button_factory(target: String) -> OnEvent {
 	let target = target.clone();
@@ -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)>,
 	mut commands: Commands,
-	ui_data: Res<UITravelInfo>,
+	query: Query<&ShopPanelProps>,
 ) -> bool {
 	let parent_id = Some(entity);
 
-	rsx! {
-		<ElementBundle>
-			<TextWidgetBundle
-				text={TextProps {
-					content: format!("Set off for:"),
-					size: 32.0,
-					..Default::default()
-				}}
-				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>
-					{
-						for (place, distance) in ui_data.travel_options.iter() {
-							constructor! {
-								<ButtonWidget
-									styles={
-										KStyle {
-											left: stretch(1.0),
-											right: stretch(1.0),
-											width: pct(70.0),
-											min_width: px(300.0),
-											max_width: px(600.0),
-											bottom: px(10.0),
-											padding_left: px(20.0),
-											padding_right: px(20.0),
+	let items = vec![
+		("Armour", 12usize, 12, 14, 2),
+		("Weapons", 1, 13, 14, 2),
+		("Luxury Goods", 13, 27, 14, 2),
+		("Ale", 11, 6, 14, 2),
+		("Milk", 2, 5, 14, 2),
+		("Cheese", 3, 6, 14, 2),
+		("Armour", 12usize, 12, 14, 2),
+		("Weapons", 1, 13, 14, 2),
+		("Luxury Goods", 13, 27, 14, 2),
+		("Ale", 11, 6, 14, 2),
+		("Milk", 2, 5, 14, 2),
+		("Cheese", 3, 6, 14, 2),
+		("Armour", 12usize, 12, 14, 2),
+		("Weapons", 1, 13, 14, 2),
+		("Luxury Goods", 13, 27, 14, 2),
+		("Ale", 11, 6, 14, 2),
+		("Milk", 2, 5, 14, 2),
+		("Cheese", 3, 6, 14, 2),
+	];
+
+	log::info!("{:?}", items);
+
+	if let Ok(props) = query.get(entity) {
+		rsx! {
+			<ElementBundle>
+				<TextWidgetBundle
+					text={TextProps {
+						content: format!("You find an array of goods to buy:"),
+						size: 32.0,
+						..Default::default()
+					}}
+					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()
-										}
-									}
-									props={ButtonWidgetProps::text(format!("{}: {}", &place, format_ui_distance(*distance)), 28.0)}
-									on_event={buysell_button_factory(place.clone())}
-								/>
-							};
+										}}
+									>
+										<InsetIconWidget props={InsetIconProps { image: IconContent::Atlas(String::from("icons"), *idx), size: 32.0}} />
+										<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>
-			</ScrollContextProviderBundle>
-		</ElementBundle>
-	};
+					</ScrollBoxBundle>
+				</ScrollContextProviderBundle>
+			</ElementBundle>
+		};
+	}
 
 	true
 }
diff --git a/game_core/src/ui/widgets/stats_panel.rs b/game_core/src/ui/widgets/stats_panel.rs
index 65f5660..7e85abf 100644
--- a/game_core/src/ui/widgets/stats_panel.rs
+++ b/game_core/src/ui/widgets/stats_panel.rs
@@ -18,13 +18,14 @@ pub fn render_stats_panel(
 	let parent_id = Some(entity);
 
 	let root_style = KStyle {
-		position_type: value(KPositionType::SelfDirected),
-		top: px(15.0),
-		left: stretch(1.0),
+		top: px(20.0),
+		left: px(20.0),
 		right: stretch(1.0),
 		bottom: stretch(1.0),
-		height: px(70.0),
+		height: px(160.0),
+		width: px(160.0),
 		layout_type: value(LayoutType::Column),
+		position_type: value(KPositionType::SelfDirected),
 		..Default::default()
 	};
 	//
@@ -40,51 +41,19 @@ pub fn render_stats_panel(
 		..Default::default()
 	};
 
+	let pad_style = KStyle {
+		render_command: value(RenderCommand::Quad),
+		height: px(20.0),
+		..Default::default()
+	};
+
 	rsx! {
 		<PanelWidget styles={root_style}>
-			<ElementBundle styles={inner_container_style}>
-				<InsetIconWidget props={InsetIconProps { size: 28.0, image: IconContent::Atlas(String::from("icons"), 14)}} />
-				<TextWidgetBundle
-					text={TextProps {
-						content: distance_travelled,
-						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>
+			<PanelStatWidget props={PanelStatProps { fill: true, text: distance_travelled, font_size: 24.0, icon: IconContent::Atlas(String::from("icons"), 14), color: Color::BLACK }} />
+			<ElementBundle styles={pad_style.clone()} />
+			<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 }} />
+			<ElementBundle styles={pad_style} />
+			<PanelStatWidget props={PanelStatProps { fill: true, text:  format!("{}", ui_data.sustenance), font_size: 24.0, icon: IconContent::Atlas(String::from("icons"), 15), color: Color::BLACK }} />
 		</PanelWidget>
 	};
 
diff --git a/game_core/src/ui/widgets/town_menu.rs b/game_core/src/ui/widgets/town_menu.rs
index 95e5024..0e2c7d7 100644
--- a/game_core/src/ui/widgets/town_menu.rs
+++ b/game_core/src/ui/widgets/town_menu.rs
@@ -23,19 +23,22 @@ pub enum TownMenuTab {
 	Travel,
 	Merchant,
 	Tavern,
+	None,
 }
 
 #[derive(Component, Clone, PartialEq, Default)]
 pub struct TownMenuPanelState {
 	pub tab: TownMenuTab,
+	pub next_tab: Option<TownMenuTab>,
 }
 
 pub fn render_town_menu_panel(
 	In((widget_context, entity)): In<(KayakWidgetContext, Entity)>,
 	mut commands: Commands,
-	state_query: Query<&TownMenuPanelState>,
+	mut state_query: Query<&mut TownMenuPanelState>,
 	ui_data: Res<UITravelInfo>,
 	places: Res<TownPaths>,
+	mut next_state: Local<TownMenuTab>,
 ) -> bool {
 	let parent_id = Some(entity);
 	let state_entity =
@@ -71,27 +74,31 @@ pub fn render_town_menu_panel(
 		&mut TownMenuPanelState,
 	>| {
 		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<
 		&mut TownMenuPanelState,
 	>| {
 		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<
 		&mut TownMenuPanelState,
 	>| {
 		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)) =
-		(&ui_data.current_town, state_query.get(state_entity).ok())
-	{
+	if let (Some(ref place), Some(mut state)) = (
+		&ui_data.current_town,
+		state_query.get_mut(state_entity).ok(),
+	) {
 		rsx! {
 			<PanelWidget
 				styles={panel_style}
@@ -151,18 +158,30 @@ pub fn render_town_menu_panel(
 
 
 				{
+					log::info!("Town menu is showing: {:?}", &state.tab);
 					match state.tab {
 						TownMenuTab::Travel => {
 							constructor! {
 								<TransitPanel />
 							};
 						},
-						TownMenuTab::Merchant => {}
+						TownMenuTab::Merchant => {
+							constructor! {
+								<ShopPanel />
+							}
+						}
 						TownMenuTab::Tavern => {}
+						TownMenuTab::None => {}
 					}
 				}
 			</PanelWidget>
 		};
+
+		if let Some(next) = state.next_tab {
+			log::info!("SWAPPGIN {:?} ;; {:?}", next, state.tab);
+			state.tab = next;
+			state.next_tab = None;
+		}
 	}
 
 	true
diff --git a/game_core/src/ui/widgets/transit_panel.rs b/game_core/src/ui/widgets/transit_panel.rs
index 4015846..e26787f 100644
--- a/game_core/src/ui/widgets/transit_panel.rs
+++ b/game_core/src/ui/widgets/transit_panel.rs
@@ -60,55 +60,58 @@ pub fn render_transit_panel(
 	In((widget_context, entity)): In<(KayakWidgetContext, Entity)>,
 	mut commands: Commands,
 	ui_data: Res<UITravelInfo>,
+	query: Query<&TransitPanelProps>,
 ) -> bool {
 	let parent_id = Some(entity);
 
-	rsx! {
-		<ElementBundle>
-			<TextWidgetBundle
-				text={TextProps {
-					content: format!("Set off for:"),
-					size: 32.0,
-					..Default::default()
-				}}
-				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>
-					{
-						for (place, distance) in ui_data.travel_options.iter() {
-							constructor! {
-								<ButtonWidget
-									styles={
-										KStyle {
-											left: stretch(1.0),
-											right: stretch(1.0),
-											width: pct(70.0),
-											min_width: px(300.0),
-											max_width: px(600.0),
-											bottom: px(10.0),
-											padding_left: px(20.0),
-											padding_right: px(20.0),
-											..Default::default()
+	if let Ok(props) = query.get(entity) {
+		rsx! {
+			<ElementBundle>
+				<TextWidgetBundle
+					text={TextProps {
+						content: format!("Set off for:"),
+						size: 32.0,
+						..Default::default()
+					}}
+					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>
+						{
+							for (place, distance) in ui_data.travel_options.iter() {
+								constructor! {
+									<ButtonWidget
+										styles={
+											KStyle {
+												left: stretch(1.0),
+												right: stretch(1.0),
+												width: pct(70.0),
+												min_width: px(300.0),
+												max_width: px(600.0),
+												bottom: px(10.0),
+												padding_left: px(20.0),
+												padding_right: px(20.0),
+												..Default::default()
+											}
 										}
-									}
-									props={ButtonWidgetProps::text(format!("{}: {}", &place, format_ui_distance(*distance)), 28.0)}
-									on_event={transit_button_factory(place.clone())}
-								/>
-							};
+										props={ButtonWidgetProps::text(format!("{}: {}", &place, format_ui_distance(*distance)), 28.0)}
+										on_event={transit_button_factory(place.clone())}
+									/>
+								};
+							}
 						}
-					}
-				</ScrollBoxBundle>
-			</ScrollContextProviderBundle>
-		</ElementBundle>
-	};
+					</ScrollBoxBundle>
+				</ScrollContextProviderBundle>
+			</ElementBundle>
+		};
+	}
 
 	true
 }
-- 
GitLab