dash-frontend: tabs, other fixes (desc)
- set rustfmt line width to 120 columns by default for wgui - dashboard tabs - wgui: `remove_children`
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1367,6 +1367,7 @@ version = "0.1.0"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"glam",
|
"glam",
|
||||||
|
"log",
|
||||||
"rust-embed",
|
"rust-embed",
|
||||||
"wgui",
|
"wgui",
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -7,4 +7,5 @@ edition = "2024"
|
|||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
wgui = { path = "../wgui/" }
|
wgui = { path = "../wgui/" }
|
||||||
glam = { workspace = true }
|
glam = { workspace = true }
|
||||||
|
log = { workspace = true }
|
||||||
rust-embed = "8.7.2"
|
rust-embed = "8.7.2"
|
||||||
|
|||||||
@@ -1,11 +1,25 @@
|
|||||||
<layout>
|
<layout>
|
||||||
<include src="theme.xml" />
|
<include src="theme.xml" />
|
||||||
<include src="templates.xml" />
|
|
||||||
|
|
||||||
<theme>
|
<theme>
|
||||||
|
<var key="side_size" value="48" />
|
||||||
<var key="side_sprite_size" value="26" />
|
<var key="side_sprite_size" value="26" />
|
||||||
|
<var key="side_button_size" value="48" />
|
||||||
</theme>
|
</theme>
|
||||||
|
|
||||||
|
<template name="SideButton">
|
||||||
|
<Button
|
||||||
|
id="${id}"
|
||||||
|
round="100%"
|
||||||
|
width="~side_button_size"
|
||||||
|
height="~side_button_size"
|
||||||
|
color="#44444400"
|
||||||
|
hover_color="#333333ff"
|
||||||
|
hover_border_color="#555555ff">
|
||||||
|
<sprite src="${src}" width="~side_sprite_size" height="~side_sprite_size" />
|
||||||
|
</Button>
|
||||||
|
</template>
|
||||||
|
|
||||||
<elements>
|
<elements>
|
||||||
<!-- background for testing -->
|
<!-- background for testing -->
|
||||||
<rectangle position="absolute" color="#333333" width="100%" height="100%" />
|
<rectangle position="absolute" color="#333333" width="100%" height="100%" />
|
||||||
@@ -13,7 +27,7 @@
|
|||||||
<!-- left/right separator (menu and rest) -->
|
<!-- left/right separator (menu and rest) -->
|
||||||
<div flex_direction="row" gap="8" width="100%" height="100%">
|
<div flex_direction="row" gap="8" width="100%" height="100%">
|
||||||
<!-- LEFT MENU -->
|
<!-- LEFT MENU -->
|
||||||
<div id="menu" width="48" min_width="48" max_width="48" height="100%" align_items="center" justify_content="center">
|
<div id="menu" width="~size_size" min_width="~side_size" max_width="~side_size" height="100%" align_items="center" justify_content="center">
|
||||||
<rectangle
|
<rectangle
|
||||||
width="100%"
|
width="100%"
|
||||||
round="100%"
|
round="100%"
|
||||||
@@ -21,17 +35,15 @@
|
|||||||
flex_direction="column"
|
flex_direction="column"
|
||||||
justify_content="center"
|
justify_content="center"
|
||||||
align_items="center"
|
align_items="center"
|
||||||
gap="24"
|
gap="4"
|
||||||
padding_top="16"
|
|
||||||
padding_bottom="16"
|
|
||||||
>
|
>
|
||||||
<sprite src="dashboard/wayvr_dashboard_mono.svg" width="~side_sprite_size" height="~side_sprite_size" />
|
<SideButton id="btn_side_home" src="dashboard/wayvr_dashboard_mono.svg" />
|
||||||
<sprite src="dashboard/apps.svg" width="~side_sprite_size" height="~side_sprite_size" />
|
<SideButton id="btn_side_apps" src="dashboard/apps.svg" />
|
||||||
<sprite src="dashboard/games.svg" width="~side_sprite_size" height="~side_sprite_size" />
|
<SideButton id="btn_side_games" src="dashboard/games.svg" />
|
||||||
<sprite src="dashboard/monado.svg" width="~side_sprite_size" height="~side_sprite_size" />
|
<SideButton id="btn_side_monado" src="dashboard/monado.svg" />
|
||||||
<sprite src="dashboard/window.svg" width="~side_sprite_size" height="~side_sprite_size" />
|
<SideButton id="btn_side_processes" src="dashboard/window.svg" />
|
||||||
<rectangle height="2" color="#FFFFFF33" width="~side_sprite_size" />
|
<rectangle height="2" color="#FFFFFF33" width="~side_sprite_size" />
|
||||||
<sprite src="dashboard/settings.svg" width="~side_sprite_size" height="~side_sprite_size" />
|
<SideButton id="btn_side_settings" src="dashboard/settings.svg" />
|
||||||
</rectangle>
|
</rectangle>
|
||||||
</div>
|
</div>
|
||||||
<!-- REST -->
|
<!-- REST -->
|
||||||
@@ -52,18 +64,7 @@
|
|||||||
flex_direction="column"
|
flex_direction="column"
|
||||||
gap="24"
|
gap="24"
|
||||||
>
|
>
|
||||||
<sprite src="dashboard/wayvr_dashboard.svg" min_width="96" min_height="96" />
|
<!-- filled-in at runtime -->
|
||||||
<label text="Hello, user!" size="32" weight="bold" color="#FFFFFF" />
|
|
||||||
<label text="Connected to wlx-overlay-s" size="16" weight="bold" color="#bbffbb" />
|
|
||||||
|
|
||||||
<!-- main button list -->
|
|
||||||
<div flex_direction="row" gap="8" margin_top="32">
|
|
||||||
<MenuButton icon="dashboard/apps.svg" text="Apps" />
|
|
||||||
<MenuButton icon="dashboard/games.svg" text="Games" />
|
|
||||||
<MenuButton icon="dashboard/monado.svg" text="Monado" />
|
|
||||||
<MenuButton icon="dashboard/window.svg" text="Processes" />
|
|
||||||
<MenuButton icon="dashboard/settings.svg" text="Settings" />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</rectangle>
|
</rectangle>
|
||||||
<!-- BOTTOM PANEL -->
|
<!-- BOTTOM PANEL -->
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
<layout>
|
<layout>
|
||||||
|
<include src="theme.xml" />
|
||||||
|
|
||||||
<template name="MenuButton">
|
<template name="MenuButton">
|
||||||
<Button
|
<Button
|
||||||
|
id="${id}"
|
||||||
width="120"
|
width="120"
|
||||||
height="82"
|
height="82"
|
||||||
color="#00000033"
|
color="#00000033"
|
||||||
7
dash-frontend/assets/gui/tab/apps.xml
Normal file
7
dash-frontend/assets/gui/tab/apps.xml
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<layout>
|
||||||
|
|
||||||
|
<elements>
|
||||||
|
<label text="Apps" size="64" weight="bold" color="#FFFFFF" />
|
||||||
|
<label text="bottom text" size="16" weight="bold" color="#FFFFFF88" />
|
||||||
|
</elements>
|
||||||
|
</layout>
|
||||||
5
dash-frontend/assets/gui/tab/games.xml
Normal file
5
dash-frontend/assets/gui/tab/games.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<layout>
|
||||||
|
<elements>
|
||||||
|
<label text="Games" size="32" weight="bold" color="#FFFFFF" />
|
||||||
|
</elements>
|
||||||
|
</layout>
|
||||||
18
dash-frontend/assets/gui/tab/home.xml
Normal file
18
dash-frontend/assets/gui/tab/home.xml
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<layout>
|
||||||
|
<include src="../t_menu_button.xml" />
|
||||||
|
|
||||||
|
<elements>
|
||||||
|
<sprite src="dashboard/wayvr_dashboard.svg" min_width="96" min_height="96" />
|
||||||
|
<label text="Hello, user!" size="32" weight="bold" color="#FFFFFF" />
|
||||||
|
<label text="Connected to wlx-overlay-s" size="16" weight="bold" color="#bbffbb" />
|
||||||
|
|
||||||
|
<!-- main button list -->
|
||||||
|
<div flex_direction="row" gap="8" margin_top="32">
|
||||||
|
<MenuButton id="btn_apps" icon="dashboard/apps.svg" text="Apps" />
|
||||||
|
<MenuButton id="btn_games" icon="dashboard/games.svg" text="Games" />
|
||||||
|
<MenuButton id="btn_monado" icon="dashboard/monado.svg" text="Monado" />
|
||||||
|
<MenuButton id="btn_processes" icon="dashboard/window.svg" text="Processes" />
|
||||||
|
<MenuButton id="btn_settings" icon="dashboard/settings.svg" text="Settings" />
|
||||||
|
</div>
|
||||||
|
</elements>
|
||||||
|
</layout>
|
||||||
5
dash-frontend/assets/gui/tab/monado.xml
Normal file
5
dash-frontend/assets/gui/tab/monado.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<layout>
|
||||||
|
<elements>
|
||||||
|
<label text="Monado" size="32" weight="bold" color="#FFFFFF" />
|
||||||
|
</elements>
|
||||||
|
</layout>
|
||||||
5
dash-frontend/assets/gui/tab/processes.xml
Normal file
5
dash-frontend/assets/gui/tab/processes.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<layout>
|
||||||
|
<elements>
|
||||||
|
<label text="Processes" size="32" weight="bold" color="#FFFFFF" />
|
||||||
|
</elements>
|
||||||
|
</layout>
|
||||||
5
dash-frontend/assets/gui/tab/settings.xml
Normal file
5
dash-frontend/assets/gui/tab/settings.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<layout>
|
||||||
|
<elements>
|
||||||
|
<label text="Settings" size="32" weight="bold" color="#FFFFFF" />
|
||||||
|
</elements>
|
||||||
|
</layout>
|
||||||
@@ -1,2 +1,3 @@
|
|||||||
tab_spaces = 2
|
tab_spaces = 2
|
||||||
hard_tabs = true
|
hard_tabs = true
|
||||||
|
max_width = 120
|
||||||
|
|||||||
@@ -1,18 +1,38 @@
|
|||||||
|
use std::{cell::RefCell, collections::VecDeque, rc::Rc};
|
||||||
|
|
||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use wgui::{
|
use wgui::{
|
||||||
|
components::button::ComponentButton,
|
||||||
event::EventListenerCollection,
|
event::EventListenerCollection,
|
||||||
globals::WguiGlobals,
|
globals::WguiGlobals,
|
||||||
layout::{Layout, LayoutParams},
|
layout::{LayoutParams, RcLayout},
|
||||||
parser::{ParseDocumentParams, ParserState},
|
parser::{ParseDocumentParams, ParserState},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::tab::{
|
||||||
|
Tab, TabParams, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, processes::TabProcesses,
|
||||||
|
settings::TabSettings,
|
||||||
|
};
|
||||||
|
|
||||||
mod assets;
|
mod assets;
|
||||||
|
mod tab;
|
||||||
|
|
||||||
pub struct Frontend {
|
pub struct Frontend {
|
||||||
pub layout: Layout,
|
pub layout: RcLayout,
|
||||||
|
globals: WguiGlobals,
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
state: ParserState,
|
state: ParserState,
|
||||||
|
|
||||||
|
current_tab: Option<Box<dyn Tab>>,
|
||||||
|
|
||||||
|
tasks: VecDeque<FrontendTask>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type RcFrontend = Rc<RefCell<Frontend>>;
|
||||||
|
|
||||||
|
pub enum FrontendTask {
|
||||||
|
SetTab(TabType),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct FrontendParams<'a> {
|
pub struct FrontendParams<'a> {
|
||||||
@@ -20,32 +40,126 @@ pub struct FrontendParams<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Frontend {
|
impl Frontend {
|
||||||
pub fn new(params: FrontendParams) -> anyhow::Result<Self> {
|
pub fn new(params: FrontendParams) -> anyhow::Result<(RcFrontend, RcLayout)> {
|
||||||
let globals = WguiGlobals::new(Box::new(assets::Asset {}))?;
|
let globals = WguiGlobals::new(Box::new(assets::Asset {}))?;
|
||||||
|
|
||||||
let (layout, state) = wgui::parser::new_layout_from_assets(
|
let (layout, state) = wgui::parser::new_layout_from_assets(
|
||||||
params.listeners,
|
params.listeners,
|
||||||
&ParseDocumentParams {
|
&ParseDocumentParams {
|
||||||
globals,
|
globals: globals.clone(),
|
||||||
path: "gui/dashboard.xml",
|
path: "gui/dashboard.xml",
|
||||||
extra: Default::default(),
|
extra: Default::default(),
|
||||||
},
|
},
|
||||||
&LayoutParams {
|
&LayoutParams { resize_to_parent: true },
|
||||||
resize_to_parent: true,
|
|
||||||
},
|
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
Ok(Self { layout, state })
|
let rc_layout = layout.as_rc();
|
||||||
|
|
||||||
|
let mut tasks = VecDeque::<FrontendTask>::new();
|
||||||
|
tasks.push_back(FrontendTask::SetTab(TabType::Home));
|
||||||
|
|
||||||
|
let res = Rc::new(RefCell::new(Self {
|
||||||
|
layout: rc_layout.clone(),
|
||||||
|
state,
|
||||||
|
current_tab: None,
|
||||||
|
globals,
|
||||||
|
tasks,
|
||||||
|
}));
|
||||||
|
|
||||||
|
Frontend::register_buttons(&res)?;
|
||||||
|
|
||||||
|
Ok((res, rc_layout))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, width: f32, height: f32, timeste_alpha: f32) -> anyhow::Result<()> {
|
pub fn update(
|
||||||
self
|
rc_this: &RcFrontend,
|
||||||
|
listeners: &mut EventListenerCollection<(), ()>,
|
||||||
|
width: f32,
|
||||||
|
height: f32,
|
||||||
|
timestep_alpha: f32,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
let mut this = rc_this.borrow_mut();
|
||||||
|
|
||||||
|
while let Some(task) = this.tasks.pop_front() {
|
||||||
|
this.process_task(rc_this, task, listeners)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
this
|
||||||
.layout
|
.layout
|
||||||
.update(Vec2::new(width, height), timeste_alpha)?;
|
.borrow_mut()
|
||||||
|
.update(Vec2::new(width, height), timestep_alpha)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn get_layout(&mut self) -> &mut Layout {
|
pub fn get_layout(&self) -> &RcLayout {
|
||||||
&mut self.layout
|
&self.layout
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push_task(&mut self, task: FrontendTask) {
|
||||||
|
self.tasks.push_back(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn process_task(
|
||||||
|
&mut self,
|
||||||
|
rc_this: &RcFrontend,
|
||||||
|
task: FrontendTask,
|
||||||
|
listeners: &mut EventListenerCollection<(), ()>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
match task {
|
||||||
|
FrontendTask::SetTab(tab_type) => self.set_tab(tab_type, rc_this, listeners)?,
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_tab(
|
||||||
|
&mut self,
|
||||||
|
tab_type: TabType,
|
||||||
|
rc_this: &RcFrontend,
|
||||||
|
listeners: &mut EventListenerCollection<(), ()>,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
|
log::info!("Setting tab to {tab_type:?}");
|
||||||
|
let mut layout = self.layout.borrow_mut();
|
||||||
|
let widget_content = self.state.fetch_widget(&layout.state, "content")?;
|
||||||
|
layout.remove_children(widget_content.id);
|
||||||
|
|
||||||
|
let tab_params = TabParams {
|
||||||
|
globals: &self.globals,
|
||||||
|
layout: &mut layout,
|
||||||
|
parent_id: widget_content.id,
|
||||||
|
listeners,
|
||||||
|
frontend: rc_this,
|
||||||
|
};
|
||||||
|
|
||||||
|
let tab: Box<dyn Tab> = match tab_type {
|
||||||
|
TabType::Home => Box::new(TabHome::new(tab_params)?),
|
||||||
|
TabType::Apps => Box::new(TabApps::new(tab_params)?),
|
||||||
|
TabType::Games => Box::new(TabGames::new(tab_params)?),
|
||||||
|
TabType::Monado => Box::new(TabMonado::new(tab_params)?),
|
||||||
|
TabType::Processes => Box::new(TabProcesses::new(tab_params)?),
|
||||||
|
TabType::Settings => Box::new(TabSettings::new(tab_params)?),
|
||||||
|
};
|
||||||
|
|
||||||
|
self.current_tab = Some(tab);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_buttons(rc_this: &RcFrontend) -> anyhow::Result<()> {
|
||||||
|
let this = rc_this.borrow_mut();
|
||||||
|
let btn_home = this.state.fetch_component_as::<ComponentButton>("btn_side_home")?;
|
||||||
|
let btn_apps = this.state.fetch_component_as::<ComponentButton>("btn_side_apps")?;
|
||||||
|
let btn_games = this.state.fetch_component_as::<ComponentButton>("btn_side_games")?;
|
||||||
|
let btn_monado = this.state.fetch_component_as::<ComponentButton>("btn_side_monado")?;
|
||||||
|
let btn_processes = this.state.fetch_component_as::<ComponentButton>("btn_side_processes")?;
|
||||||
|
let btn_settings = this.state.fetch_component_as::<ComponentButton>("btn_side_settings")?;
|
||||||
|
|
||||||
|
TabType::register_button(rc_this.clone(), &btn_home, TabType::Home);
|
||||||
|
TabType::register_button(rc_this.clone(), &btn_apps, TabType::Apps);
|
||||||
|
TabType::register_button(rc_this.clone(), &btn_games, TabType::Games);
|
||||||
|
TabType::register_button(rc_this.clone(), &btn_monado, TabType::Monado);
|
||||||
|
TabType::register_button(rc_this.clone(), &btn_processes, TabType::Processes);
|
||||||
|
TabType::register_button(rc_this.clone(), &btn_settings, TabType::Settings);
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
31
dash-frontend/src/tab/apps.rs
Normal file
31
dash-frontend/src/tab/apps.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use wgui::parser::{ParseDocumentParams, ParserState};
|
||||||
|
|
||||||
|
use crate::tab::{Tab, TabParams, TabType};
|
||||||
|
|
||||||
|
pub struct TabApps {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub state: ParserState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tab for TabApps {
|
||||||
|
fn get_type(&self) -> TabType {
|
||||||
|
TabType::Apps
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TabApps {
|
||||||
|
pub fn new(params: TabParams) -> anyhow::Result<Self> {
|
||||||
|
let state = wgui::parser::parse_from_assets(
|
||||||
|
&ParseDocumentParams {
|
||||||
|
globals: params.globals.clone(),
|
||||||
|
path: "gui/tab/apps.xml",
|
||||||
|
extra: Default::default(),
|
||||||
|
},
|
||||||
|
params.layout,
|
||||||
|
params.listeners,
|
||||||
|
params.parent_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Self { state })
|
||||||
|
}
|
||||||
|
}
|
||||||
31
dash-frontend/src/tab/games.rs
Normal file
31
dash-frontend/src/tab/games.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use wgui::parser::{ParseDocumentParams, ParserState};
|
||||||
|
|
||||||
|
use crate::tab::{Tab, TabParams, TabType};
|
||||||
|
|
||||||
|
pub struct TabGames {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub state: ParserState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tab for TabGames {
|
||||||
|
fn get_type(&self) -> TabType {
|
||||||
|
TabType::Games
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TabGames {
|
||||||
|
pub fn new(params: TabParams) -> anyhow::Result<Self> {
|
||||||
|
let state = wgui::parser::parse_from_assets(
|
||||||
|
&ParseDocumentParams {
|
||||||
|
globals: params.globals.clone(),
|
||||||
|
path: "gui/tab/games.xml",
|
||||||
|
extra: Default::default(),
|
||||||
|
},
|
||||||
|
params.layout,
|
||||||
|
params.listeners,
|
||||||
|
params.parent_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Self { state })
|
||||||
|
}
|
||||||
|
}
|
||||||
47
dash-frontend/src/tab/home.rs
Normal file
47
dash-frontend/src/tab/home.rs
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
use wgui::{
|
||||||
|
components::button::ComponentButton,
|
||||||
|
parser::{ParseDocumentParams, ParserState},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::tab::{Tab, TabParams, TabType};
|
||||||
|
|
||||||
|
pub struct TabHome {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub state: ParserState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tab for TabHome {
|
||||||
|
fn get_type(&self) -> TabType {
|
||||||
|
TabType::Home
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TabHome {
|
||||||
|
pub fn new(params: TabParams) -> anyhow::Result<Self> {
|
||||||
|
let state = wgui::parser::parse_from_assets(
|
||||||
|
&ParseDocumentParams {
|
||||||
|
globals: params.globals.clone(),
|
||||||
|
path: "gui/tab/home.xml",
|
||||||
|
extra: Default::default(),
|
||||||
|
},
|
||||||
|
params.layout,
|
||||||
|
params.listeners,
|
||||||
|
params.parent_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let btn_apps = state.fetch_component_as::<ComponentButton>("btn_apps")?;
|
||||||
|
let btn_games = state.fetch_component_as::<ComponentButton>("btn_games")?;
|
||||||
|
let btn_monado = state.fetch_component_as::<ComponentButton>("btn_monado")?;
|
||||||
|
let btn_processes = state.fetch_component_as::<ComponentButton>("btn_processes")?;
|
||||||
|
let btn_settings = state.fetch_component_as::<ComponentButton>("btn_settings")?;
|
||||||
|
|
||||||
|
let frontend = params.frontend;
|
||||||
|
TabType::register_button(frontend.clone(), &btn_apps, TabType::Apps);
|
||||||
|
TabType::register_button(frontend.clone(), &btn_games, TabType::Games);
|
||||||
|
TabType::register_button(frontend.clone(), &btn_monado, TabType::Monado);
|
||||||
|
TabType::register_button(frontend.clone(), &btn_processes, TabType::Processes);
|
||||||
|
TabType::register_button(frontend.clone(), &btn_settings, TabType::Settings);
|
||||||
|
|
||||||
|
Ok(Self { state })
|
||||||
|
}
|
||||||
|
}
|
||||||
51
dash-frontend/src/tab/mod.rs
Normal file
51
dash-frontend/src/tab/mod.rs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use wgui::{
|
||||||
|
components::button::ComponentButton,
|
||||||
|
event::EventListenerCollection,
|
||||||
|
globals::WguiGlobals,
|
||||||
|
layout::{Layout, WidgetID},
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{FrontendTask, RcFrontend};
|
||||||
|
|
||||||
|
pub mod apps;
|
||||||
|
pub mod games;
|
||||||
|
pub mod home;
|
||||||
|
pub mod monado;
|
||||||
|
pub mod processes;
|
||||||
|
pub mod settings;
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, Debug)]
|
||||||
|
pub enum TabType {
|
||||||
|
Home,
|
||||||
|
Apps,
|
||||||
|
Games,
|
||||||
|
Monado,
|
||||||
|
Processes,
|
||||||
|
Settings,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TabParams<'a> {
|
||||||
|
pub globals: &'a WguiGlobals,
|
||||||
|
pub layout: &'a mut Layout,
|
||||||
|
pub parent_id: WidgetID,
|
||||||
|
pub frontend: &'a RcFrontend,
|
||||||
|
pub listeners: &'a mut EventListenerCollection<(), ()>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Tab {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fn get_type(&self) -> TabType;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TabType {
|
||||||
|
pub fn register_button(this_rc: RcFrontend, btn: &Rc<ComponentButton>, tab: TabType) {
|
||||||
|
btn.on_click({
|
||||||
|
Box::new(move |_evt| {
|
||||||
|
this_rc.borrow_mut().push_task(FrontendTask::SetTab(tab));
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
31
dash-frontend/src/tab/monado.rs
Normal file
31
dash-frontend/src/tab/monado.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use wgui::parser::{ParseDocumentParams, ParserState};
|
||||||
|
|
||||||
|
use crate::tab::{Tab, TabParams, TabType};
|
||||||
|
|
||||||
|
pub struct TabMonado {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub state: ParserState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tab for TabMonado {
|
||||||
|
fn get_type(&self) -> TabType {
|
||||||
|
TabType::Games
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TabMonado {
|
||||||
|
pub fn new(params: TabParams) -> anyhow::Result<Self> {
|
||||||
|
let state = wgui::parser::parse_from_assets(
|
||||||
|
&ParseDocumentParams {
|
||||||
|
globals: params.globals.clone(),
|
||||||
|
path: "gui/tab/monado.xml",
|
||||||
|
extra: Default::default(),
|
||||||
|
},
|
||||||
|
params.layout,
|
||||||
|
params.listeners,
|
||||||
|
params.parent_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Self { state })
|
||||||
|
}
|
||||||
|
}
|
||||||
31
dash-frontend/src/tab/processes.rs
Normal file
31
dash-frontend/src/tab/processes.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use wgui::parser::{ParseDocumentParams, ParserState};
|
||||||
|
|
||||||
|
use crate::tab::{Tab, TabParams, TabType};
|
||||||
|
|
||||||
|
pub struct TabProcesses {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub state: ParserState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tab for TabProcesses {
|
||||||
|
fn get_type(&self) -> TabType {
|
||||||
|
TabType::Games
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TabProcesses {
|
||||||
|
pub fn new(params: TabParams) -> anyhow::Result<Self> {
|
||||||
|
let state = wgui::parser::parse_from_assets(
|
||||||
|
&ParseDocumentParams {
|
||||||
|
globals: params.globals.clone(),
|
||||||
|
path: "gui/tab/processes.xml",
|
||||||
|
extra: Default::default(),
|
||||||
|
},
|
||||||
|
params.layout,
|
||||||
|
params.listeners,
|
||||||
|
params.parent_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Self { state })
|
||||||
|
}
|
||||||
|
}
|
||||||
31
dash-frontend/src/tab/settings.rs
Normal file
31
dash-frontend/src/tab/settings.rs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
use wgui::parser::{ParseDocumentParams, ParserState};
|
||||||
|
|
||||||
|
use crate::tab::{Tab, TabParams, TabType};
|
||||||
|
|
||||||
|
pub struct TabSettings {
|
||||||
|
#[allow(dead_code)]
|
||||||
|
pub state: ParserState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Tab for TabSettings {
|
||||||
|
fn get_type(&self) -> TabType {
|
||||||
|
TabType::Settings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TabSettings {
|
||||||
|
pub fn new(params: TabParams) -> anyhow::Result<Self> {
|
||||||
|
let state = wgui::parser::parse_from_assets(
|
||||||
|
&ParseDocumentParams {
|
||||||
|
globals: params.globals.clone(),
|
||||||
|
path: "gui/tab/settings.xml",
|
||||||
|
extra: Default::default(),
|
||||||
|
},
|
||||||
|
params.layout,
|
||||||
|
params.listeners,
|
||||||
|
params.parent_id,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
Ok(Self { state })
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -31,7 +31,9 @@ use winit::{
|
|||||||
keyboard::{KeyCode, PhysicalKey},
|
keyboard::{KeyCode, PhysicalKey},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::testbed::{testbed_dashboard::TestbedDashboard, testbed_generic::TestbedGeneric};
|
use crate::testbed::{
|
||||||
|
TestbedUpdateParams, testbed_dashboard::TestbedDashboard, testbed_generic::TestbedGeneric,
|
||||||
|
};
|
||||||
|
|
||||||
mod assets;
|
mod assets;
|
||||||
mod profiler;
|
mod profiler;
|
||||||
@@ -124,6 +126,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
} => match delta {
|
} => match delta {
|
||||||
MouseScrollDelta::LineDelta(x, y) => testbed
|
MouseScrollDelta::LineDelta(x, y) => testbed
|
||||||
.layout()
|
.layout()
|
||||||
|
.borrow_mut()
|
||||||
.push_event(
|
.push_event(
|
||||||
&mut listeners,
|
&mut listeners,
|
||||||
&wgui::event::Event::MouseWheel(MouseWheelEvent {
|
&wgui::event::Event::MouseWheel(MouseWheelEvent {
|
||||||
@@ -136,6 +139,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
.unwrap(),
|
.unwrap(),
|
||||||
MouseScrollDelta::PixelDelta(pos) => testbed
|
MouseScrollDelta::PixelDelta(pos) => testbed
|
||||||
.layout()
|
.layout()
|
||||||
|
.borrow_mut()
|
||||||
.push_event(
|
.push_event(
|
||||||
&mut listeners,
|
&mut listeners,
|
||||||
&wgui::event::Event::MouseWheel(MouseWheelEvent {
|
&wgui::event::Event::MouseWheel(MouseWheelEvent {
|
||||||
@@ -155,6 +159,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
if matches!(state, winit::event::ElementState::Pressed) {
|
if matches!(state, winit::event::ElementState::Pressed) {
|
||||||
testbed
|
testbed
|
||||||
.layout()
|
.layout()
|
||||||
|
.borrow_mut()
|
||||||
.push_event(
|
.push_event(
|
||||||
&mut listeners,
|
&mut listeners,
|
||||||
&wgui::event::Event::MouseDown(MouseDownEvent {
|
&wgui::event::Event::MouseDown(MouseDownEvent {
|
||||||
@@ -168,6 +173,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
} else {
|
} else {
|
||||||
testbed
|
testbed
|
||||||
.layout()
|
.layout()
|
||||||
|
.borrow_mut()
|
||||||
.push_event(
|
.push_event(
|
||||||
&mut listeners,
|
&mut listeners,
|
||||||
&wgui::event::Event::MouseUp(MouseUpEvent {
|
&wgui::event::Event::MouseUp(MouseUpEvent {
|
||||||
@@ -188,6 +194,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
mouse = vec2(position.x as _, position.y as _);
|
mouse = vec2(position.x as _, position.y as _);
|
||||||
testbed
|
testbed
|
||||||
.layout()
|
.layout()
|
||||||
|
.borrow_mut()
|
||||||
.push_event(
|
.push_event(
|
||||||
&mut listeners,
|
&mut listeners,
|
||||||
&wgui::event::Event::MouseMotion(MouseMotionEvent {
|
&wgui::event::Event::MouseMotion(MouseMotionEvent {
|
||||||
@@ -261,18 +268,19 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
while timestep.on_tick() {
|
while timestep.on_tick() {
|
||||||
testbed.layout().tick().unwrap();
|
testbed.layout().borrow_mut().tick().unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
testbed
|
testbed
|
||||||
.update(
|
.update(TestbedUpdateParams {
|
||||||
(swapchain_size[0] as f32 / scale) as _,
|
listeners: &mut listeners,
|
||||||
(swapchain_size[1] as f32 / scale) as _,
|
width: (swapchain_size[0] as f32 / scale) as _,
|
||||||
timestep.alpha,
|
height: (swapchain_size[1] as f32 / scale) as _,
|
||||||
)
|
timestep_alpha: timestep.alpha,
|
||||||
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
if !render_context.dirty && !testbed.layout().check_toggle_needs_redraw() {
|
if !render_context.dirty && !testbed.layout().borrow_mut().check_toggle_needs_redraw() {
|
||||||
// no need to redraw
|
// no need to redraw
|
||||||
std::thread::sleep(std::time::Duration::from_millis(5)); // dirty fix to prevent cpu burning precious cycles doing a busy loop
|
std::thread::sleep(std::time::Duration::from_millis(5)); // dirty fix to prevent cpu burning precious cycles doing a busy loop
|
||||||
return;
|
return;
|
||||||
@@ -301,7 +309,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
.unwrap();
|
.unwrap();
|
||||||
cmd_buf.begin_rendering(tgt).unwrap();
|
cmd_buf.begin_rendering(tgt).unwrap();
|
||||||
|
|
||||||
let primitives = wgui::drawing::draw(testbed.layout()).unwrap();
|
let primitives = wgui::drawing::draw(&testbed.layout().borrow_mut()).unwrap();
|
||||||
render_context
|
render_context
|
||||||
.draw(&mut shared_context, &mut cmd_buf, &primitives)
|
.draw(&mut shared_context, &mut cmd_buf, &primitives)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|||||||
@@ -1,10 +1,17 @@
|
|||||||
use wgui::layout::Layout;
|
use wgui::{event::EventListenerCollection, layout::RcLayout};
|
||||||
|
|
||||||
pub mod testbed_any;
|
pub mod testbed_any;
|
||||||
pub mod testbed_dashboard;
|
pub mod testbed_dashboard;
|
||||||
pub mod testbed_generic;
|
pub mod testbed_generic;
|
||||||
|
|
||||||
pub trait Testbed {
|
pub struct TestbedUpdateParams<'a> {
|
||||||
fn update(&mut self, width: f32, height: f32, timestep_alpha: f32) -> anyhow::Result<()>;
|
pub listeners: &'a mut EventListenerCollection<(), ()>,
|
||||||
fn layout(&mut self) -> &mut Layout;
|
pub width: f32,
|
||||||
|
pub height: f32,
|
||||||
|
pub timestep_alpha: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait Testbed {
|
||||||
|
fn update(&mut self, params: TestbedUpdateParams) -> anyhow::Result<()>;
|
||||||
|
fn layout(&self) -> &RcLayout;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,14 +1,17 @@
|
|||||||
use crate::{assets, testbed::Testbed};
|
use crate::{
|
||||||
|
assets,
|
||||||
|
testbed::{Testbed, TestbedUpdateParams},
|
||||||
|
};
|
||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use wgui::{
|
use wgui::{
|
||||||
event::EventListenerCollection,
|
event::EventListenerCollection,
|
||||||
globals::WguiGlobals,
|
globals::WguiGlobals,
|
||||||
layout::{Layout, LayoutParams},
|
layout::{LayoutParams, RcLayout},
|
||||||
parser::{ParseDocumentParams, ParserState},
|
parser::{ParseDocumentParams, ParserState},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct TestbedAny {
|
pub struct TestbedAny {
|
||||||
pub layout: Layout,
|
pub layout: RcLayout,
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
state: ParserState,
|
state: ParserState,
|
||||||
@@ -29,19 +32,23 @@ impl TestbedAny {
|
|||||||
},
|
},
|
||||||
&LayoutParams::default(),
|
&LayoutParams::default(),
|
||||||
)?;
|
)?;
|
||||||
Ok(Self { layout, state })
|
Ok(Self {
|
||||||
|
layout: layout.as_rc(),
|
||||||
|
state,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Testbed for TestbedAny {
|
impl Testbed for TestbedAny {
|
||||||
fn update(&mut self, width: f32, height: f32, timestep_alpha: f32) -> anyhow::Result<()> {
|
fn update(&mut self, params: TestbedUpdateParams) -> anyhow::Result<()> {
|
||||||
self
|
self.layout.borrow_mut().update(
|
||||||
.layout
|
Vec2::new(params.width, params.height),
|
||||||
.update(Vec2::new(width, height), timestep_alpha)?;
|
params.timestep_alpha,
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(&mut self) -> &mut Layout {
|
fn layout(&self) -> &RcLayout {
|
||||||
&mut self.layout
|
&self.layout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,32 @@
|
|||||||
use crate::testbed::Testbed;
|
use crate::testbed::{Testbed, TestbedUpdateParams};
|
||||||
use wgui::{event::EventListenerCollection, layout::Layout};
|
use dash_frontend::Frontend;
|
||||||
|
use wgui::{event::EventListenerCollection, layout::RcLayout};
|
||||||
|
|
||||||
pub struct TestbedDashboard {
|
pub struct TestbedDashboard {
|
||||||
frontend: dash_frontend::Frontend,
|
layout: RcLayout,
|
||||||
|
frontend: dash_frontend::RcFrontend,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TestbedDashboard {
|
impl TestbedDashboard {
|
||||||
pub fn new(listeners: &mut EventListenerCollection<(), ()>) -> anyhow::Result<Self> {
|
pub fn new(listeners: &mut EventListenerCollection<(), ()>) -> anyhow::Result<Self> {
|
||||||
Ok(Self {
|
let (frontend, layout) =
|
||||||
frontend: dash_frontend::Frontend::new(dash_frontend::FrontendParams { listeners })?,
|
dash_frontend::Frontend::new(dash_frontend::FrontendParams { listeners })?;
|
||||||
})
|
Ok(Self { frontend, layout })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Testbed for TestbedDashboard {
|
impl Testbed for TestbedDashboard {
|
||||||
fn update(&mut self, width: f32, height: f32, timestep_alpha: f32) -> anyhow::Result<()> {
|
fn update(&mut self, params: TestbedUpdateParams) -> anyhow::Result<()> {
|
||||||
self.frontend.update(width, height, timestep_alpha)?;
|
Frontend::update(
|
||||||
Ok(())
|
&self.frontend,
|
||||||
|
params.listeners,
|
||||||
|
params.width,
|
||||||
|
params.height,
|
||||||
|
params.timestep_alpha,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(&mut self) -> &mut Layout {
|
fn layout(&self) -> &RcLayout {
|
||||||
self.frontend.get_layout()
|
&self.layout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use crate::{assets, testbed::Testbed};
|
use crate::{
|
||||||
|
assets,
|
||||||
|
testbed::{Testbed, TestbedUpdateParams},
|
||||||
|
};
|
||||||
use glam::Vec2;
|
use glam::Vec2;
|
||||||
use wgui::{
|
use wgui::{
|
||||||
components::{
|
components::{
|
||||||
@@ -12,13 +15,13 @@ use wgui::{
|
|||||||
event::EventListenerCollection,
|
event::EventListenerCollection,
|
||||||
globals::WguiGlobals,
|
globals::WguiGlobals,
|
||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::{Layout, LayoutParams, Widget},
|
layout::{LayoutParams, RcLayout, Widget},
|
||||||
parser::{ParseDocumentExtra, ParseDocumentParams, ParserState},
|
parser::{ParseDocumentExtra, ParseDocumentParams, ParserState},
|
||||||
widget::{label::WidgetLabel, rectangle::WidgetRectangle},
|
widget::{label::WidgetLabel, rectangle::WidgetRectangle},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct TestbedGeneric {
|
pub struct TestbedGeneric {
|
||||||
pub layout: Layout,
|
pub layout: RcLayout,
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
state: ParserState,
|
state: ParserState,
|
||||||
@@ -103,12 +106,16 @@ impl TestbedGeneric {
|
|||||||
let button_aqua = state.fetch_component_as::<ComponentButton>("button_aqua")?;
|
let button_aqua = state.fetch_component_as::<ComponentButton>("button_aqua")?;
|
||||||
let button_yellow = state.fetch_component_as::<ComponentButton>("button_yellow")?;
|
let button_yellow = state.fetch_component_as::<ComponentButton>("button_yellow")?;
|
||||||
|
|
||||||
handle_button_click(button_red, label_cur_option.clone(), "Clicked red");
|
handle_button_click(button_red, label_cur_option.widget.clone(), "Clicked red");
|
||||||
handle_button_click(button_aqua, label_cur_option.clone(), "Clicked aqua");
|
handle_button_click(button_aqua, label_cur_option.widget.clone(), "Clicked aqua");
|
||||||
handle_button_click(button_yellow, label_cur_option.clone(), "Clicked yellow");
|
handle_button_click(
|
||||||
|
button_yellow,
|
||||||
|
label_cur_option.widget.clone(),
|
||||||
|
"Clicked yellow",
|
||||||
|
);
|
||||||
|
|
||||||
let cb_first = state.fetch_component_as::<ComponentCheckbox>("cb_first")?;
|
let cb_first = state.fetch_component_as::<ComponentCheckbox>("cb_first")?;
|
||||||
let label = label_cur_option.clone();
|
let label = label_cur_option.widget.clone();
|
||||||
cb_first.on_toggle(Box::new(move |e| {
|
cb_first.on_toggle(Box::new(move |e| {
|
||||||
let mut widget = label.get_as_mut::<WidgetLabel>();
|
let mut widget = label.get_as_mut::<WidgetLabel>();
|
||||||
widget.set_text(
|
widget.set_text(
|
||||||
@@ -118,19 +125,23 @@ impl TestbedGeneric {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}));
|
}));
|
||||||
|
|
||||||
Ok(Self { layout, state })
|
Ok(Self {
|
||||||
|
layout: layout.as_rc(),
|
||||||
|
state,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Testbed for TestbedGeneric {
|
impl Testbed for TestbedGeneric {
|
||||||
fn update(&mut self, width: f32, height: f32, timestep_alpha: f32) -> anyhow::Result<()> {
|
fn update(&mut self, params: TestbedUpdateParams) -> anyhow::Result<()> {
|
||||||
self
|
self.layout.borrow_mut().update(
|
||||||
.layout
|
Vec2::new(params.width, params.height),
|
||||||
.update(Vec2::new(width, height), timestep_alpha)?;
|
params.timestep_alpha,
|
||||||
|
)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(&mut self) -> &mut Layout {
|
fn layout(&self) -> &RcLayout {
|
||||||
&mut self.layout
|
&self.layout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,2 +1,3 @@
|
|||||||
tab_spaces = 2
|
tab_spaces = 2
|
||||||
hard_tabs = true
|
hard_tabs = true
|
||||||
|
max_width = 120
|
||||||
|
|||||||
@@ -43,6 +43,11 @@ impl Widget {
|
|||||||
pub struct WidgetMap(HopSlotMap<WidgetID, Widget>);
|
pub struct WidgetMap(HopSlotMap<WidgetID, Widget>);
|
||||||
pub type WidgetNodeMap = SecondaryMap<WidgetID, taffy::NodeId>;
|
pub type WidgetNodeMap = SecondaryMap<WidgetID, taffy::NodeId>;
|
||||||
|
|
||||||
|
pub struct WidgetPair {
|
||||||
|
pub id: WidgetID,
|
||||||
|
pub widget: Widget,
|
||||||
|
}
|
||||||
|
|
||||||
impl WidgetMap {
|
impl WidgetMap {
|
||||||
fn new() -> Self {
|
fn new() -> Self {
|
||||||
Self(HopSlotMap::with_key())
|
Self(HopSlotMap::with_key())
|
||||||
@@ -60,6 +65,10 @@ impl WidgetMap {
|
|||||||
self.0.insert(obj)
|
self.0.insert(obj)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn remove_single(&mut self, handle: WidgetID) {
|
||||||
|
self.0.remove(handle);
|
||||||
|
}
|
||||||
|
|
||||||
// cast to specific widget type, does nothing if widget ID is expired
|
// cast to specific widget type, does nothing if widget ID is expired
|
||||||
// panics in case if the widget type is wrong
|
// panics in case if the widget type is wrong
|
||||||
// TODO: panic-less alternative
|
// TODO: panic-less alternative
|
||||||
@@ -101,6 +110,8 @@ pub struct Layout {
|
|||||||
pub animations: Animations,
|
pub animations: Animations,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub type RcLayout = Rc<RefCell<Layout>>;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct LayoutParams {
|
pub struct LayoutParams {
|
||||||
pub resize_to_parent: bool,
|
pub resize_to_parent: bool,
|
||||||
@@ -128,6 +139,10 @@ fn add_child_internal(
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Layout {
|
impl Layout {
|
||||||
|
pub fn as_rc(self) -> RcLayout {
|
||||||
|
Rc::new(RefCell::new(self))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn add_child(
|
pub fn add_child(
|
||||||
&mut self,
|
&mut self,
|
||||||
parent_widget_id: WidgetID,
|
parent_widget_id: WidgetID,
|
||||||
@@ -148,6 +163,34 @@ impl Layout {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn collect_children_ids_recursive(&self, widget_id: WidgetID, out: &mut Vec<(WidgetID, taffy::NodeId)>) {
|
||||||
|
let Some(node_id) = self.state.nodes.get(widget_id) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
for child_id in self.state.tree.child_ids(*node_id) {
|
||||||
|
let child_widget_id = self.state.tree.get_node_context(child_id).unwrap();
|
||||||
|
out.push((*child_widget_id, child_id));
|
||||||
|
self.collect_children_ids_recursive(*child_widget_id, out);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// removes all children of a specific widget
|
||||||
|
pub fn remove_children(&mut self, widget_id: WidgetID) {
|
||||||
|
let mut ids = Vec::new();
|
||||||
|
self.collect_children_ids_recursive(widget_id, &mut ids);
|
||||||
|
|
||||||
|
if !ids.is_empty() {
|
||||||
|
self.needs_redraw = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (widget_id, node_id) in ids {
|
||||||
|
self.state.widgets.remove_single(widget_id);
|
||||||
|
self.state.nodes.remove(widget_id);
|
||||||
|
self.state.tree.remove(node_id).unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn process_pending_components(&mut self) -> anyhow::Result<()> {
|
fn process_pending_components(&mut self) -> anyhow::Result<()> {
|
||||||
let mut alterables = EventAlterables::default();
|
let mut alterables = EventAlterables::default();
|
||||||
|
|
||||||
@@ -224,20 +267,11 @@ impl Layout {
|
|||||||
|
|
||||||
let mut widget = widget.0.borrow_mut();
|
let mut widget = widget.0.borrow_mut();
|
||||||
|
|
||||||
match widget.process_event(
|
match widget.process_event(widget_id, listeners_vec, node_id, event, user_data, &mut params)? {
|
||||||
widget_id,
|
|
||||||
listeners_vec,
|
|
||||||
node_id,
|
|
||||||
event,
|
|
||||||
user_data,
|
|
||||||
&mut params,
|
|
||||||
)? {
|
|
||||||
widget::EventResult::Pass => {
|
widget::EventResult::Pass => {
|
||||||
// go on
|
// go on
|
||||||
}
|
}
|
||||||
widget::EventResult::Consumed
|
widget::EventResult::Consumed | widget::EventResult::Outside | widget::EventResult::Unused => {
|
||||||
| widget::EventResult::Outside
|
|
||||||
| widget::EventResult::Unused => {
|
|
||||||
iter_children = false;
|
iter_children = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -279,13 +313,7 @@ impl Layout {
|
|||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
let mut alterables = EventAlterables::default();
|
let mut alterables = EventAlterables::default();
|
||||||
|
|
||||||
self.push_event_widget(
|
self.push_event_widget(listeners, self.root_node, event, &mut alterables, &mut user_data)?;
|
||||||
listeners,
|
|
||||||
self.root_node,
|
|
||||||
event,
|
|
||||||
&mut alterables,
|
|
||||||
&mut user_data,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
self.process_alterables(alterables)?;
|
self.process_alterables(alterables)?;
|
||||||
|
|
||||||
@@ -360,10 +388,7 @@ impl Layout {
|
|||||||
None => taffy::Size::ZERO,
|
None => taffy::Size::ZERO,
|
||||||
Some(h) => {
|
Some(h) => {
|
||||||
if let Some(w) = self.state.widgets.get(*h) {
|
if let Some(w) = self.state.widgets.get(*h) {
|
||||||
w.0
|
w.0.borrow_mut().obj.measure(known_dimensions, available_space)
|
||||||
.borrow_mut()
|
|
||||||
.obj
|
|
||||||
.measure(known_dimensions, available_space)
|
|
||||||
} else {
|
} else {
|
||||||
taffy::Size::ZERO
|
taffy::Size::ZERO
|
||||||
}
|
}
|
||||||
@@ -388,9 +413,7 @@ impl Layout {
|
|||||||
pub fn update(&mut self, size: Vec2, timestep_alpha: f32) -> anyhow::Result<()> {
|
pub fn update(&mut self, size: Vec2, timestep_alpha: f32) -> anyhow::Result<()> {
|
||||||
let mut alterables = EventAlterables::default();
|
let mut alterables = EventAlterables::default();
|
||||||
|
|
||||||
self
|
self.animations.process(&self.state, &mut alterables, timestep_alpha);
|
||||||
.animations
|
|
||||||
.process(&self.state, &mut alterables, timestep_alpha);
|
|
||||||
|
|
||||||
self.process_alterables(alterables)?;
|
self.process_alterables(alterables)?;
|
||||||
self.try_recompute_layout(size)?;
|
self.try_recompute_layout(size)?;
|
||||||
@@ -428,11 +451,7 @@ impl Layout {
|
|||||||
|
|
||||||
for request in alterables.style_set_requests {
|
for request in alterables.style_set_requests {
|
||||||
if let Err(e) = self.state.tree.set_style(request.0, request.1) {
|
if let Err(e) = self.state.tree.set_style(request.0, request.1) {
|
||||||
log::error!(
|
log::error!("failed to set style for taffy widget ID {:?}: {:?}", request.0, e);
|
||||||
"failed to set style for taffy widget ID {:?}: {:?}",
|
|
||||||
request.0,
|
|
||||||
e
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ use crate::{
|
|||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{
|
parser::{
|
||||||
ParserContext, ParserFile, iter_attribs, parse_check_f32, parse_check_i32, parse_children,
|
ParserContext, ParserFile, iter_attribs, parse_check_f32, parse_check_i32, process_component, style::parse_style,
|
||||||
process_component, style::parse_style,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,7 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
components::{Component, slider},
|
components::{Component, slider},
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{
|
parser::{ParserContext, ParserFile, iter_attribs, parse_check_f32, process_component, style::parse_style},
|
||||||
ParserContext, ParserFile, iter_attribs, parse_check_f32, parse_children, process_component,
|
|
||||||
style::parse_style,
|
|
||||||
},
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn parse_component_slider<'a, U1, U2>(
|
pub fn parse_component_slider<'a, U1, U2>(
|
||||||
|
|||||||
@@ -13,12 +13,11 @@ use crate::{
|
|||||||
drawing::{self},
|
drawing::{self},
|
||||||
event::EventListenerCollection,
|
event::EventListenerCollection,
|
||||||
globals::WguiGlobals,
|
globals::WguiGlobals,
|
||||||
layout::{Layout, LayoutParams, LayoutState, Widget, WidgetID, WidgetMap},
|
layout::{Layout, LayoutParams, LayoutState, Widget, WidgetID, WidgetMap, WidgetPair},
|
||||||
parser::{
|
parser::{
|
||||||
component_button::parse_component_button, component_checkbox::parse_component_checkbox,
|
component_button::parse_component_button, component_checkbox::parse_component_checkbox,
|
||||||
component_slider::parse_component_slider, widget_div::parse_widget_div,
|
component_slider::parse_component_slider, widget_div::parse_widget_div, widget_label::parse_widget_label,
|
||||||
widget_label::parse_widget_label, widget_rectangle::parse_widget_rectangle,
|
widget_rectangle::parse_widget_rectangle, widget_sprite::parse_widget_sprite,
|
||||||
widget_sprite::parse_widget_sprite,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
use ouroboros::self_referencing;
|
use ouroboros::self_referencing;
|
||||||
@@ -95,13 +94,16 @@ impl ParserState {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn fetch_widget(&self, state: &LayoutState, id: &str) -> anyhow::Result<Widget> {
|
pub fn fetch_widget(&self, state: &LayoutState, id: &str) -> anyhow::Result<WidgetPair> {
|
||||||
let widget_id = self.get_widget_id(id)?;
|
let widget_id = self.get_widget_id(id)?;
|
||||||
let widget = state
|
let widget = state
|
||||||
.widgets
|
.widgets
|
||||||
.get(widget_id)
|
.get(widget_id)
|
||||||
.ok_or_else(|| anyhow::anyhow!("fetch_widget({}): widget not found", id))?;
|
.ok_or_else(|| anyhow::anyhow!("fetch_widget({}): widget not found", id))?;
|
||||||
Ok(widget.clone())
|
Ok(WidgetPair {
|
||||||
|
id: widget_id,
|
||||||
|
widget: widget.clone(),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn process_template<U1, U2>(
|
pub fn process_template<U1, U2>(
|
||||||
@@ -121,9 +123,9 @@ impl ParserState {
|
|||||||
layout,
|
layout,
|
||||||
listeners,
|
listeners,
|
||||||
ids: Default::default(),
|
ids: Default::default(),
|
||||||
macro_attribs: self.macro_attribs.clone(), // FIXME: prevent copying
|
macro_attribs: self.macro_attribs.clone(), // FIXME: prevent copying
|
||||||
var_map: self.var_map.clone(), // FIXME: prevent copying
|
var_map: self.var_map.clone(), // FIXME: prevent copying
|
||||||
components: self.components.clone(), // FIXME: prevent copying
|
components: self.components.clone(), // FIXME: prevent copying
|
||||||
components_id_map: self.components_id_map.clone(), // FIXME: prevent copying
|
components_id_map: self.components_id_map.clone(), // FIXME: prevent copying
|
||||||
templates: Default::default(),
|
templates: Default::default(),
|
||||||
doc_params,
|
doc_params,
|
||||||
@@ -135,13 +137,7 @@ impl ParserState {
|
|||||||
template_parameters: template_parameters.clone(), // FIXME: prevent copying
|
template_parameters: template_parameters.clone(), // FIXME: prevent copying
|
||||||
};
|
};
|
||||||
|
|
||||||
parse_widget_other_internal(
|
parse_widget_other_internal(&template.clone(), template_parameters, &file, &mut ctx, widget_id)?;
|
||||||
&template.clone(),
|
|
||||||
template_parameters,
|
|
||||||
&file,
|
|
||||||
&mut ctx,
|
|
||||||
widget_id,
|
|
||||||
)?;
|
|
||||||
|
|
||||||
// FIXME?
|
// FIXME?
|
||||||
ctx.ids.into_iter().for_each(|(id, key)| {
|
ctx.ids.into_iter().for_each(|(id, key)| {
|
||||||
@@ -203,19 +199,11 @@ pub fn parse_color_hex(html_hex: &str) -> Option<drawing::Color> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_tag_by_name<'a>(
|
fn get_tag_by_name<'a>(node: &roxmltree::Node<'a, 'a>, name: &str) -> Option<roxmltree::Node<'a, 'a>> {
|
||||||
node: &roxmltree::Node<'a, 'a>,
|
node.children().find(|&child| child.tag_name().name() == name)
|
||||||
name: &str,
|
|
||||||
) -> Option<roxmltree::Node<'a, 'a>> {
|
|
||||||
node
|
|
||||||
.children()
|
|
||||||
.find(|&child| child.tag_name().name() == name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn require_tag_by_name<'a>(
|
fn require_tag_by_name<'a>(node: &roxmltree::Node<'a, 'a>, name: &str) -> anyhow::Result<roxmltree::Node<'a, 'a>> {
|
||||||
node: &roxmltree::Node<'a, 'a>,
|
|
||||||
name: &str,
|
|
||||||
) -> anyhow::Result<roxmltree::Node<'a, 'a>> {
|
|
||||||
get_tag_by_name(node, name).ok_or_else(|| anyhow::anyhow!("Tag \"{}\" not found", name))
|
get_tag_by_name(node, name).ok_or_else(|| anyhow::anyhow!("Tag \"{}\" not found", name))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -332,8 +320,7 @@ fn parse_widget_other<'a, U1, U2>(
|
|||||||
return Ok(()); // not critical
|
return Ok(()); // not critical
|
||||||
};
|
};
|
||||||
|
|
||||||
let template_parameters: HashMap<Rc<str>, Rc<str>> =
|
let template_parameters: HashMap<Rc<str>, Rc<str>> = iter_attribs(file, ctx, &node, false).collect();
|
||||||
iter_attribs(file, ctx, &node, false).collect();
|
|
||||||
|
|
||||||
parse_widget_other_internal(&template.clone(), template_parameters, file, ctx, parent_id)
|
parse_widget_other_internal(&template.clone(), template_parameters, file, ctx, parent_id)
|
||||||
}
|
}
|
||||||
@@ -350,11 +337,7 @@ fn parse_tag_include<'a, U1, U2>(
|
|||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
match key {
|
match key {
|
||||||
"src" => {
|
"src" => {
|
||||||
let mut new_path = file
|
let mut new_path = file.path.parent().unwrap_or_else(|| Path::new("/")).to_path_buf();
|
||||||
.path
|
|
||||||
.parent()
|
|
||||||
.unwrap_or_else(|| Path::new("/"))
|
|
||||||
.to_path_buf();
|
|
||||||
new_path.push(value);
|
new_path.push(value);
|
||||||
|
|
||||||
let (new_file, node_layout) = get_doc_from_path(ctx, &new_path)?;
|
let (new_file, node_layout) = get_doc_from_path(ctx, &new_path)?;
|
||||||
@@ -443,10 +426,7 @@ fn process_attrib<'a, U1, U2>(
|
|||||||
},
|
},
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
(
|
(Rc::from(key), replace_vars(value, &file.template_parameters))
|
||||||
Rc::from(key),
|
|
||||||
replace_vars(value, &file.template_parameters),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -501,11 +481,7 @@ fn parse_tag_theme<'a, U1, U2>(ctx: &mut ParserContext<U1, U2>, node: roxmltree:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_tag_template<U1, U2>(
|
fn parse_tag_template<U1, U2>(file: &ParserFile, ctx: &mut ParserContext<U1, U2>, node: roxmltree::Node<'_, '_>) {
|
||||||
file: &ParserFile,
|
|
||||||
ctx: &mut ParserContext<U1, U2>,
|
|
||||||
node: roxmltree::Node<'_, '_>,
|
|
||||||
) {
|
|
||||||
let mut template_name: Option<Rc<str>> = None;
|
let mut template_name: Option<Rc<str>> = None;
|
||||||
|
|
||||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
let attribs: Vec<_> = iter_attribs(file, ctx, &node, false).collect();
|
||||||
@@ -535,11 +511,7 @@ fn parse_tag_template<U1, U2>(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_tag_macro<U1, U2>(
|
fn parse_tag_macro<U1, U2>(file: &ParserFile, ctx: &mut ParserContext<U1, U2>, node: roxmltree::Node<'_, '_>) {
|
||||||
file: &ParserFile,
|
|
||||||
ctx: &mut ParserContext<U1, U2>,
|
|
||||||
node: roxmltree::Node<'_, '_>,
|
|
||||||
) {
|
|
||||||
let mut macro_name: Option<Rc<str>> = None;
|
let mut macro_name: Option<Rc<str>> = None;
|
||||||
|
|
||||||
let attribs: Vec<_> = iter_attribs(file, ctx, &node, true).collect();
|
let attribs: Vec<_> = iter_attribs(file, ctx, &node, true).collect();
|
||||||
@@ -563,12 +535,7 @@ fn parse_tag_macro<U1, U2>(
|
|||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
|
|
||||||
ctx.macro_attribs.insert(
|
ctx.macro_attribs.insert(name, MacroAttribs { attribs: macro_attribs });
|
||||||
name,
|
|
||||||
MacroAttribs {
|
|
||||||
attribs: macro_attribs,
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_component<'a, U1, U2>(
|
fn process_component<'a, U1, U2>(
|
||||||
@@ -583,11 +550,7 @@ fn process_component<'a, U1, U2>(
|
|||||||
#[allow(clippy::single_match)]
|
#[allow(clippy::single_match)]
|
||||||
match key.as_ref() {
|
match key.as_ref() {
|
||||||
"id" => {
|
"id" => {
|
||||||
if ctx
|
if ctx.components_id_map.insert(value.clone(), component.weak()).is_some() {
|
||||||
.components_id_map
|
|
||||||
.insert(value.clone(), component.weak())
|
|
||||||
.is_some()
|
|
||||||
{
|
|
||||||
log::warn!("duplicate component ID \"{value}\" in the same layout file!");
|
log::warn!("duplicate component ID \"{value}\" in the same layout file!");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,8 +6,8 @@ use crate::{
|
|||||||
any::AnyTrait,
|
any::AnyTrait,
|
||||||
drawing,
|
drawing,
|
||||||
event::{
|
event::{
|
||||||
self, CallbackData, CallbackDataCommon, CallbackMetadata, Event, EventAlterables,
|
self, CallbackData, CallbackDataCommon, CallbackMetadata, Event, EventAlterables, EventListenerKind,
|
||||||
EventListenerKind, EventListenerVec, MouseWheelEvent,
|
EventListenerVec, MouseWheelEvent,
|
||||||
},
|
},
|
||||||
layout::{Layout, LayoutState, WidgetID},
|
layout::{Layout, LayoutState, WidgetID},
|
||||||
transform_stack::TransformStack,
|
transform_stack::TransformStack,
|
||||||
@@ -138,8 +138,8 @@ impl EventParams<'_> {
|
|||||||
pub enum EventResult {
|
pub enum EventResult {
|
||||||
Pass, // widget acknowledged it and allows the event to pass to the children
|
Pass, // widget acknowledged it and allows the event to pass to the children
|
||||||
Consumed, // widget triggered an action, do not pass to children
|
Consumed, // widget triggered an action, do not pass to children
|
||||||
Outside, // widget acknowledged this event but ignores it due the fact the mouse is not hovered over it
|
Outside, // widget acknowledged this event but ignores it due the fact the mouse is not hovered over it
|
||||||
Unused, // widget doesn't have any events attached
|
Unused, // widget doesn't have any events attached
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_scroll_enabled(style: &taffy::Style) -> (bool, bool) {
|
fn get_scroll_enabled(style: &taffy::Style) -> (bool, bool) {
|
||||||
@@ -223,12 +223,7 @@ impl WidgetState {
|
|||||||
self.obj.draw(state, params);
|
self.obj.draw(state, params);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn draw_scrollbars(
|
pub fn draw_scrollbars(&mut self, state: &mut DrawState, params: &DrawParams, info: &ScrollbarInfo) {
|
||||||
&mut self,
|
|
||||||
state: &mut DrawState,
|
|
||||||
params: &DrawParams,
|
|
||||||
info: &ScrollbarInfo,
|
|
||||||
) {
|
|
||||||
let (enabled_horiz, enabled_vert) = get_scroll_enabled(params.style);
|
let (enabled_horiz, enabled_vert) = get_scroll_enabled(params.style);
|
||||||
if !enabled_horiz && !enabled_vert {
|
if !enabled_horiz && !enabled_vert {
|
||||||
return;
|
return;
|
||||||
|
|||||||
Reference in New Issue
Block a user