Skip to content
Snippets Groups Projects
utilities.rs 8.46 KiB
Newer Older
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;

#[macro_export]
macro_rules! empty_props {
	($name: ident) => {
		#[derive(::bevy::prelude::Component, Clone, PartialEq, Default)]
		#[repr(transparent)]
		pub struct $name($crate::ui::utilities::EmptyProps);
		impl kayak_ui::prelude::Widget for $name {}
	};
}

#[macro_export]
macro_rules! basic_widget {
	($props: ident => $name: ident) => {
		#[derive(bevy::prelude::Bundle)]
		pub struct $name {
			pub props: $props,
			pub name: ::kayak_ui::prelude::WidgetName,
			pub styles: ::kayak_ui::prelude::KStyle,
			pub computed_styles: ::kayak_ui::prelude::ComputedStyles,
		}

		impl std::default::Default for $name {
			fn default() -> $name {
				let props = $props::default();
				$name {
					name: kayak_ui::prelude::Widget::get_name(&props),
					props,
					styles: ::kayak_ui::prelude::KStyle::default(),
					computed_styles: ::kayak_ui::prelude::ComputedStyles::default(),
				}
			}
		}
	};
}
#[macro_export]
macro_rules! parent_widget {
	($props: ident => $name: ident) => {
		#[derive(bevy::prelude::Bundle)]
		pub struct $name {
			pub props: $props,
			pub name: ::kayak_ui::prelude::WidgetName,
			pub styles: ::kayak_ui::prelude::KStyle,
			pub computed_styles: ::kayak_ui::prelude::ComputedStyles,
			pub children: ::kayak_ui::prelude::KChildren,
			pub on_event: ::kayak_ui::prelude::OnEvent,
		}

		impl std::default::Default for $name {
			fn default() -> $name {
				let props = $props::default();
				$name {
					name: kayak_ui::prelude::Widget::get_name(&props),
					props,
					styles: ::kayak_ui::prelude::KStyle::default(),
					computed_styles: ::kayak_ui::prelude::ComputedStyles::default(),
					children: ::kayak_ui::prelude::KChildren::default(),
					on_event: ::kayak_ui::prelude::OnEvent::default(),
				}
			}
		}
	};
}

#[macro_export]
macro_rules! register_widget {
	($ctx: expr, $props: ident, $state: ident, $system: ident) => {{
		$ctx.add_widget_data::<$props, $state>();
		$ctx.add_widget_system(
			::kayak_ui::prelude::Widget::get_name(&$props::default()),
			::kayak_ui::prelude::widget_update::<$props, $state>,
			$system,
		);
	}};
}

#[macro_export]
macro_rules! register_widget_with_update {
	($ctx: expr, $props: ident, $state: ident, $system: ident, $update: expr) => {{
		$ctx.add_widget_data::<$props, $state>();
		$ctx.add_widget_system(
			::kayak_ui::prelude::Widget::get_name(&$props::default()),
			$update,
			$system,
		);
	}};
}

#[macro_export]
macro_rules! register_widget_with_resource {
	($ctx: expr, $props: ident, $state: ident, $resource: ident, $system: ident) => {{
		$ctx.add_widget_data::<$props, $state>();
		$ctx.add_widget_system(
			::kayak_ui::prelude::Widget::get_name(&$props::default()),
			$crate::ui::utilities::widget_update_with_resource::<$props, $state, $resource>,
			$system,
		);
	}};
}

#[macro_export]
macro_rules! register_widget_with_many_resources {
	($ctx: expr, $props: ident, $state: ident, $system: ident, $($name: ident: $resource: ident),+) => {{
		$ctx.add_widget_data::<$props, $state>();
		$ctx.add_widget_system(
			::kayak_ui::prelude::Widget::get_name(&$props::default()),
			|
				::bevy::prelude::In((widget_context, entity, previous_entity)): ::bevy::prelude::In<(::kayak_ui::prelude::KayakWidgetContext, ::bevy::prelude::Entity, ::bevy::prelude::Entity)>,
				$( $name: ::bevy::prelude::Res<$resource> ),+,
				widget_param: ::kayak_ui::prelude::WidgetParam<$props, $state>,
			| {
				widget_param.has_changed(&widget_context, entity, previous_entity)
					$( || $name.is_changed() )+
			},
			$system,
		);
	}};
}

pub fn widget_update_with_resource<
	Props: PartialEq + Component + Clone,
	State: PartialEq + Component + Clone,
	External: Resource,
>(
	In((widget_context, entity, previous_entity)): In<(KayakWidgetContext, Entity, Entity)>,
	resource: Res<External>,
	widget_param: WidgetParam<Props, State>,
) -> bool {
	widget_param.has_changed(&widget_context, entity, previous_entity) || resource.is_changed()
}

#[derive(Component)]
pub struct StateUIRoot;

fn remove_root_ui(
	mut commands: Commands,
	query: Query<Entity, (With<KayakRootContext>, With<StateUIRoot>)>,
) {
	for entity in &query {
		commands.entity(entity).despawn_recursive();
	}
}

pub mod context {
	use kayak_ui::prelude::{widget_update, EmptyState, KayakRootContext};
	use kayak_ui::widgets::KayakWidgetsContextPlugin;
	use kayak_ui::KayakUIPlugin;

	use crate::register_widget;
	use crate::ui::components::*;
	use crate::ui::sync::{UIStatsData, UITradeData, UITravelInfo};
	use crate::ui::widgets::*;
	use crate::world::EncounterState;

	pub fn create_root_context() -> KayakRootContext {
		let mut widget_context = KayakRootContext::new();
		widget_context.add_plugin(KayakWidgetsContextPlugin);
		widget_context.add_plugin(AdventWidgetsPlugin);
		widget_context
	}

	pub struct AdventWidgetsPlugin;
	impl KayakUIPlugin for AdventWidgetsPlugin {
		fn build(&self, widget_context: &mut KayakRootContext) {
			register_widget!(
				widget_context,
				DebugInfoProps,
				EmptyState,
				render_debug_info
			);
			register_widget!(widget_context, PanelProps, EmptyState, render_panel_widget);
			register_widget!(
				widget_context,
				ButtonWidgetProps,
				ButtonWidgetState,
				render_button_widget
			);
			register_widget!(
				widget_context,
				ImageButtonWidgetProps,
				ImageButtonWidgetState,
				render_image_button_widget
			);
			register_widget!(
				widget_context,
				VDividerWidgetProps,
				EmptyState,
				render_v_divider
			);
			register_widget!(
				widget_context,
				HDividerWidgetProps,
				EmptyState,
				render_h_divider
			);
			register_widget!(
				widget_context,
				ATextBoxProps,
				ATextBoxState,
				render_text_box_widget
			);
			register_widget!(
				widget_context,
				InsetIconProps,
				EmptyState,
				render_inset_icon_widget
			);
			register_widget!(
				widget_context,
				PanelStatProps,
				EmptyState,
				render_panel_stat_widget
			);
			register_widget_with_many_resources!(
				widget_context,
				TownMenuPanelProps,
Louis's avatar
Louis committed
				TownMenuPanelState,
				render_town_menu_panel,
				travelinfo: UITravelInfo,
				statsinfo: UIStatsData
Louis's avatar
Louis committed
			register_widget_with_resource!(
				widget_context,
				TransitPanelProps,
				EmptyState,
				UITravelInfo,
				render_transit_panel
			);
			register_widget_with_resource!(
				widget_context,
				ShopPanelProps,
				EmptyState,
				UITradeData,
			register_widget_with_resource!(
				widget_context,
				TavernPanelProps,
				EmptyState,
				UITradeData,
				render_tavern_panel
			);
			register_widget_with_resource!(
				widget_context,
				StatsPanelProps,
				EmptyState,
				UIStatsData,
				render_stats_panel
			);
			register_widget_with_resource!(
				widget_context,
				EncounterPanelProps,
				EmptyState,
				EncounterState,
				render_encounter_panel
			);
		}
	}
}

pub type OnEventParams = In<(EventDispatcherContext, WidgetState, Event, Entity)>;

#[macro_export]
macro_rules! on_button_click {
	($param_type: ty, $func_body: expr) => {
		::kayak_ui::prelude::OnEvent::new(
			move |::bevy::prelude::In((
				event_dispatcher_context,
				_widget_state,
				event,
				_entity,
			)): ::bevy::prelude::In<(
				::kayak_ui::prelude::EventDispatcherContext,
				::kayak_ui::prelude::WidgetState,
				::kayak_ui::prelude::Event,
				::bevy::prelude::Entity,
			)>,
			      params: $param_type| {
				match event.event_type {
					::kayak_ui::prelude::EventType::Click(..) => {
						$func_body(params);
					}
					_ => {}
				}

				(event_dispatcher_context, event)
			},
		)
	};
}

#[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()
	}
}