use bevy::prelude::{ Commands, Component, DespawnRecursiveExt, Entity, In, Query, Res, Resource, With, }; use kayak_ui::prelude::{ Event, EventDispatcherContext, KayakRootContext, KayakWidgetContext, WidgetParam, WidgetState, }; #[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()), | In((widget_context, entity, previous_entity)): In<(KayakWidgetContext, Entity, Entity)>, $( $name: bevy::prelude::Res<$resource> ),+, widget_param: 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::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, ATextBoxProps, ATextBoxState, render_text_box_widget ); register_widget!( widget_context, InsetIconProps, EmptyState, render_inset_icon_widget ); register_widget_with_resource!( widget_context, TownMenuPanelProps, TownMenuPanelState, UITravelInfo, render_town_menu_panel ); register_widget_with_resource!( widget_context, TransitPanelProps, EmptyState, UITravelInfo, render_transit_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) }, ) }; }