diff --git a/dash-frontend/assets/gui/dashboard.xml b/dash-frontend/assets/gui/dashboard.xml
index 9a46400..59b624f 100644
--- a/dash-frontend/assets/gui/dashboard.xml
+++ b/dash-frontend/assets/gui/dashboard.xml
@@ -80,6 +80,7 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dash-frontend/assets/gui/view/popup_window.xml b/dash-frontend/assets/gui/view/popup_window.xml
new file mode 100644
index 0000000..bb6dc22
--- /dev/null
+++ b/dash-frontend/assets/gui/view/popup_window.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dash-frontend/src/frontend.rs b/dash-frontend/src/frontend.rs
index 0b5c7d9..5e18b1d 100644
--- a/dash-frontend/src/frontend.rs
+++ b/dash-frontend/src/frontend.rs
@@ -19,8 +19,14 @@ use crate::{
Tab, TabParams, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, processes::TabProcesses,
settings::TabSettings,
},
+ util::popup_manager::{PopupManager, PopupManagerParams},
};
+pub struct FrontendWidgets {
+ pub id_label_time: WidgetID,
+ pub id_rect_content: WidgetID,
+}
+
pub struct Frontend {
pub layout: RcLayout,
globals: WguiGlobals,
@@ -36,8 +42,8 @@ pub struct Frontend {
ticks: u32,
- id_label_time: WidgetID,
- id_rect_content: WidgetID,
+ widgets: FrontendWidgets,
+ popup_manager: PopupManager,
}
pub struct InitParams {
@@ -50,6 +56,7 @@ pub enum FrontendTask {
SetTab(TabType),
RefreshClock,
RefreshBackground,
+ MountPopup,
}
impl Frontend {
@@ -71,7 +78,7 @@ impl Frontend {
},
)?;
- let (layout, state) = wgui::parser::new_layout_from_assets(
+ let (mut layout, state) = wgui::parser::new_layout_from_assets(
&ParseDocumentParams {
globals: globals.clone(),
path: AssetPath::BuiltIn("gui/dashboard.xml"),
@@ -80,6 +87,13 @@ impl Frontend {
&LayoutParams { resize_to_parent: true },
)?;
+ let id_popup_manager = state.get_widget_id("popup_manager")?;
+ let popup_manager = PopupManager::new(PopupManagerParams {
+ globals: globals.clone(),
+ layout: &mut layout,
+ parent_id: id_popup_manager,
+ })?;
+
let rc_layout = layout.as_rc();
let mut tasks = VecDeque::::new();
@@ -95,9 +109,12 @@ impl Frontend {
globals,
tasks,
ticks: 0,
- id_label_time,
- id_rect_content,
+ widgets: FrontendWidgets {
+ id_label_time,
+ id_rect_content,
+ },
settings: params.settings,
+ popup_manager,
};
// init some things first
@@ -142,7 +159,7 @@ impl Frontend {
let mut common = c.common();
{
- let Some(mut label) = common.state.widgets.get_as::(self.id_label_time) else {
+ let Some(mut label) = common.state.widgets.get_as::(self.widgets.id_label_time) else {
anyhow::bail!("");
};
@@ -165,10 +182,22 @@ impl Frontend {
Ok(())
}
+ fn mount_popup(&mut self) -> anyhow::Result<()> {
+ let mut layout = self.layout.borrow_mut();
+
+ self.popup_manager.push_popup(self.globals.clone(), &mut layout)?;
+
+ Ok(())
+ }
+
fn update_background(&self) -> anyhow::Result<()> {
let layout = self.layout.borrow_mut();
- let Some(mut rect) = layout.state.widgets.get_as::(self.id_rect_content) else {
+ let Some(mut rect) = layout
+ .state
+ .widgets
+ .get_as::(self.widgets.id_rect_content)
+ else {
anyhow::bail!("");
};
@@ -197,6 +226,7 @@ impl Frontend {
FrontendTask::SetTab(tab_type) => self.set_tab(tab_type, rc_this)?,
FrontendTask::RefreshClock => self.update_time()?,
FrontendTask::RefreshBackground => self.update_background()?,
+ FrontendTask::MountPopup => self.mount_popup()?,
}
Ok(())
}
@@ -212,6 +242,7 @@ impl Frontend {
layout: &mut layout,
parent_id: widget_content.id,
frontend: rc_this,
+ frontend_widgets: &self.widgets,
settings: self.settings.get_mut(),
};
diff --git a/dash-frontend/src/lib.rs b/dash-frontend/src/lib.rs
index 00dd643..5266836 100644
--- a/dash-frontend/src/lib.rs
+++ b/dash-frontend/src/lib.rs
@@ -4,3 +4,4 @@ pub mod settings;
mod tab;
mod util;
mod various;
+mod views;
diff --git a/dash-frontend/src/tab/apps.rs b/dash-frontend/src/tab/apps.rs
index c60f59a..63aa2a8 100644
--- a/dash-frontend/src/tab/apps.rs
+++ b/dash-frontend/src/tab/apps.rs
@@ -8,8 +8,10 @@ use wgui::{
};
use crate::{
+ frontend::FrontendTask,
tab::{Tab, TabParams, TabType},
util::{self, desktop_finder::DesktopEntry},
+ views,
};
pub struct TabApps {
@@ -95,10 +97,14 @@ impl AppList {
let data = parser_state.parse_template(doc_params, "AppEntry", params.layout, list_parent.id, template_params)?;
let button = data.fetch_component_as::("button")?;
- button.on_click(Box::new(move |_common, _evt| {
- log::info!("click");
- Ok(())
- }));
+
+ button.on_click({
+ let frontend = params.frontend.clone();
+ Box::new(move |_common, _evt| {
+ frontend.borrow_mut().push_task(FrontendTask::MountPopup);
+ Ok(())
+ })
+ });
self.data.push(data);
diff --git a/dash-frontend/src/tab/mod.rs b/dash-frontend/src/tab/mod.rs
index 9139ea7..2ab13e7 100644
--- a/dash-frontend/src/tab/mod.rs
+++ b/dash-frontend/src/tab/mod.rs
@@ -6,7 +6,7 @@ use wgui::{
layout::{Layout, WidgetID},
};
-use crate::frontend::{FrontendTask, RcFrontend};
+use crate::frontend::{FrontendTask, FrontendWidgets, RcFrontend};
pub mod apps;
pub mod games;
@@ -30,6 +30,7 @@ pub struct TabParams<'a> {
pub layout: &'a mut Layout,
pub parent_id: WidgetID,
pub frontend: &'a RcFrontend,
+ pub frontend_widgets: &'a FrontendWidgets,
pub settings: &'a mut crate::settings::Settings,
}
diff --git a/dash-frontend/src/util/desktop_finder.rs b/dash-frontend/src/util/desktop_finder.rs
index bdb6caa..8f0b6b5 100644
--- a/dash-frontend/src/util/desktop_finder.rs
+++ b/dash-frontend/src/util/desktop_finder.rs
@@ -1,7 +1,7 @@
use gio::prelude::{AppInfoExt, IconExt};
use gtk::traits::IconThemeExt;
-#[derive(Debug)]
+#[derive(Debug, Clone)]
#[allow(dead_code)] // TODO: remove this
pub struct DesktopEntry {
pub exec_path: String,
diff --git a/dash-frontend/src/util/mod.rs b/dash-frontend/src/util/mod.rs
index b37699f..7e87a39 100644
--- a/dash-frontend/src/util/mod.rs
+++ b/dash-frontend/src/util/mod.rs
@@ -1 +1,2 @@
pub mod desktop_finder;
+pub mod popup_manager;
diff --git a/dash-frontend/src/util/popup_manager.rs b/dash-frontend/src/util/popup_manager.rs
new file mode 100644
index 0000000..71f3f3f
--- /dev/null
+++ b/dash-frontend/src/util/popup_manager.rs
@@ -0,0 +1,110 @@
+use std::{
+ cell::RefCell,
+ rc::{Rc, Weak},
+};
+
+use wgui::{
+ assets::AssetPath,
+ components::button::ComponentButton,
+ event::{EventAlterables, StyleSetRequest},
+ globals::WguiGlobals,
+ layout::{Layout, LayoutTask, WidgetID},
+ parser::{Fetchable, ParseDocumentParams, ParserState},
+ taffy::Display,
+};
+
+pub struct PopupManagerParams<'a> {
+ pub globals: WguiGlobals,
+ pub layout: &'a mut Layout,
+ pub parent_id: WidgetID,
+}
+
+struct MountedPopup {
+ #[allow(dead_code)]
+ state: ParserState,
+ id_root: WidgetID,
+}
+
+pub struct State {
+ popup_stack: Vec,
+}
+
+pub struct PopupManager {
+ pub state: Rc>,
+ globals: WguiGlobals,
+ parent_id: WidgetID,
+}
+
+pub struct PushPopupResult {
+ pub id_content: WidgetID,
+}
+
+impl State {
+ fn refresh_stack(&mut self, alterables: &mut EventAlterables) {
+ // show only the topmost popup
+ for (idx, popup) in self.popup_stack.iter().enumerate() {
+ alterables.set_style(
+ popup.id_root,
+ StyleSetRequest::Display(if idx == self.popup_stack.len() - 1 {
+ Display::Flex
+ } else {
+ Display::None
+ }),
+ );
+ }
+ }
+
+ fn pop_popup(&mut self, alterables: &mut EventAlterables) {
+ let Some(popup) = self.popup_stack.pop() else {
+ return;
+ };
+
+ alterables.tasks.push(LayoutTask::RemoveWidget(popup.id_root));
+ self.refresh_stack(alterables);
+ }
+}
+
+impl PopupManager {
+ pub fn new(params: PopupManagerParams) -> anyhow::Result {
+ Ok(Self {
+ globals: params.globals,
+ parent_id: params.parent_id,
+ state: Rc::new(RefCell::new(State {
+ popup_stack: Vec::new(),
+ })),
+ })
+ }
+
+ pub fn push_popup(&mut self, globals: WguiGlobals, layout: &mut Layout) -> anyhow::Result {
+ let doc_params = &ParseDocumentParams {
+ globals,
+ path: AssetPath::BuiltIn("gui/view/popup_window.xml"),
+ extra: Default::default(),
+ };
+ let state = wgui::parser::parse_from_assets(doc_params, layout, self.parent_id)?;
+
+ let id_root = state.get_widget_id("root")?;
+ let id_content = state.get_widget_id("content")?;
+
+ let but_back = state.fetch_component_as::("but_back")?;
+
+ but_back.on_click({
+ let state = self.state.clone();
+ Box::new(move |common, _evt| {
+ state.borrow_mut().pop_popup(common.alterables);
+ Ok(())
+ })
+ });
+
+ let mounted_popup = MountedPopup { state, id_root };
+
+ let mut state = self.state.borrow_mut();
+ state.popup_stack.push(mounted_popup);
+
+ let mut c = layout.start_common();
+ state.refresh_stack(c.common().alterables);
+ c.finish()?;
+
+ Ok(PushPopupResult { id_content })
+ }
+}
diff --git a/dash-frontend/src/views/app_launcher.rs b/dash-frontend/src/views/app_launcher.rs
new file mode 100644
index 0000000..49d169f
--- /dev/null
+++ b/dash-frontend/src/views/app_launcher.rs
@@ -0,0 +1,39 @@
+use wgui::{
+ assets::AssetPath,
+ globals::WguiGlobals,
+ layout::{Layout, WidgetID},
+ parser::{ParseDocumentParams, ParserState},
+};
+
+use crate::util::desktop_finder::DesktopEntry;
+
+pub struct View {
+ #[allow(dead_code)]
+ pub state: ParserState,
+
+ entry: DesktopEntry,
+}
+
+pub struct Params<'a> {
+ pub globals: WguiGlobals,
+ pub entry: DesktopEntry,
+ pub layout: &'a mut Layout,
+ pub parent_id: WidgetID,
+}
+
+impl View {
+ pub fn new(params: Params) -> anyhow::Result {
+ let doc_params = &ParseDocumentParams {
+ globals: params.globals.clone(),
+ path: AssetPath::BuiltIn("gui/view/app_launcher.xml"),
+ extra: Default::default(),
+ };
+
+ let mut state = wgui::parser::parse_from_assets(doc_params, params.layout, params.parent_id)?;
+
+ Ok(Self {
+ entry: params.entry,
+ state,
+ })
+ }
+}
diff --git a/dash-frontend/src/views/mod.rs b/dash-frontend/src/views/mod.rs
new file mode 100644
index 0000000..5eb7e4e
--- /dev/null
+++ b/dash-frontend/src/views/mod.rs
@@ -0,0 +1 @@
+pub mod app_launcher;