App launcher view
This commit is contained in:
@@ -2,8 +2,6 @@
|
|||||||
<include src="theme.xml" />
|
<include src="theme.xml" />
|
||||||
|
|
||||||
<macro name="group_box"
|
<macro name="group_box"
|
||||||
min_width="200"
|
|
||||||
flex_grow="1"
|
|
||||||
flex_direction="column"
|
flex_direction="column"
|
||||||
align_items="baseline"
|
align_items="baseline"
|
||||||
border="2"
|
border="2"
|
||||||
|
|||||||
5
dash-frontend/assets/gui/t_separator.xml
Normal file
5
dash-frontend/assets/gui/t_separator.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<layout>
|
||||||
|
<template name="Separator">
|
||||||
|
<rectangle width="100%" height="1" color="#FFFFFF77" />
|
||||||
|
</template>
|
||||||
|
</layout>
|
||||||
@@ -1,7 +1,40 @@
|
|||||||
<layout>
|
<layout>
|
||||||
|
<template name="Subtext">
|
||||||
|
<div flex_direction="row" gap="8">
|
||||||
|
<label weight="bold" text="${title}" />
|
||||||
|
<label text="foo" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template name="ApplicationIcon">
|
||||||
|
<sprite src_ext="${path}" width="128" height="128" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<include src="../t_separator.xml" />
|
||||||
|
<include src="../t_group_box.xml" />
|
||||||
|
|
||||||
<elements>
|
<elements>
|
||||||
<rectangle new_pass="1" color="#ff000099" width="100%" height="100%">
|
<div flex_direction="row" gap="16" width="100%">
|
||||||
<label text="TEST" size="80" />
|
<rectangle macro="group_box" id="icon_parent" height="100%" padding="8" color="#0033aa66" color2="#00000022" gradient="vertical" justify_content="center">
|
||||||
</rectangle>
|
|
||||||
|
</rectangle>
|
||||||
|
<div flex_direction="column" gap="8" width="100%" align_items="baseline">
|
||||||
|
<label id="label_title" weight="bold" size="32" />
|
||||||
|
<Subtext title="Exec:" />
|
||||||
|
<Subtext title="Args:" />
|
||||||
|
<Separator />
|
||||||
|
<CheckBox text="Run in X11 mode (cage)" />
|
||||||
|
<CheckBox text="Run in Wayland mode" checked="1" />
|
||||||
|
<Separator />
|
||||||
|
<Button color="#44ce22FF" padding_top="4" padding_bottom="4" round="8" padding_right="12">
|
||||||
|
<sprite src="dashboard/play.svg" width="32" height="32" />
|
||||||
|
<label text="Launch embedded" weight="bold" size="17" shadow="#00000099" />
|
||||||
|
</Button>
|
||||||
|
<Separator />
|
||||||
|
<rectangle macro="group_box">
|
||||||
|
<label size="16" weight="bold" text="Or launch it detached" />
|
||||||
|
</rectangle>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</elements>
|
</elements>
|
||||||
</layout>
|
</layout>
|
||||||
@@ -28,14 +28,14 @@
|
|||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<label id="popup_title" weight="bold" size="18" text="Pop-up title" />
|
<label id="popup_title" weight="bold" size="18" />
|
||||||
</div>
|
</div>
|
||||||
</rectangle>
|
</rectangle>
|
||||||
|
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
<rectangle width="100%" height="100%"
|
<rectangle width="100%" height="100%"
|
||||||
color="#010310cc"
|
color="#010310ee"
|
||||||
color2="#061e4acc"
|
color2="#062a5eee"
|
||||||
gradient="vertical"
|
gradient="vertical"
|
||||||
padding="16"
|
padding="16"
|
||||||
id="content">
|
id="content">
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ use crate::{
|
|||||||
Tab, TabParams, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, processes::TabProcesses,
|
Tab, TabParams, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, processes::TabProcesses,
|
||||||
settings::TabSettings,
|
settings::TabSettings,
|
||||||
},
|
},
|
||||||
util::popup_manager::{PopupManager, PopupManagerParams},
|
util::popup_manager::{MountPopupParams, PopupManager, PopupManagerParams},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct FrontendWidgets {
|
pub struct FrontendWidgets {
|
||||||
@@ -27,6 +27,19 @@ pub struct FrontendWidgets {
|
|||||||
pub id_rect_content: WidgetID,
|
pub id_rect_content: WidgetID,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct FrontendTasks(pub Rc<RefCell<VecDeque<FrontendTask>>>);
|
||||||
|
|
||||||
|
impl FrontendTasks {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self(Rc::new(RefCell::new(VecDeque::new())))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn push(&self, task: FrontendTask) {
|
||||||
|
self.0.borrow_mut().push_back(task);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Frontend {
|
pub struct Frontend {
|
||||||
pub layout: RcLayout,
|
pub layout: RcLayout,
|
||||||
globals: WguiGlobals,
|
globals: WguiGlobals,
|
||||||
@@ -38,7 +51,7 @@ pub struct Frontend {
|
|||||||
|
|
||||||
current_tab: Option<Box<dyn Tab>>,
|
current_tab: Option<Box<dyn Tab>>,
|
||||||
|
|
||||||
tasks: VecDeque<FrontendTask>,
|
pub tasks: FrontendTasks,
|
||||||
|
|
||||||
ticks: u32,
|
ticks: u32,
|
||||||
|
|
||||||
@@ -56,7 +69,8 @@ pub enum FrontendTask {
|
|||||||
SetTab(TabType),
|
SetTab(TabType),
|
||||||
RefreshClock,
|
RefreshClock,
|
||||||
RefreshBackground,
|
RefreshBackground,
|
||||||
MountPopup,
|
MountPopup(MountPopupParams),
|
||||||
|
RefreshPopupManager,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Frontend {
|
impl Frontend {
|
||||||
@@ -96,8 +110,8 @@ impl Frontend {
|
|||||||
|
|
||||||
let rc_layout = layout.as_rc();
|
let rc_layout = layout.as_rc();
|
||||||
|
|
||||||
let mut tasks = VecDeque::<FrontendTask>::new();
|
let tasks = FrontendTasks::new();
|
||||||
tasks.push_back(FrontendTask::SetTab(TabType::Home));
|
tasks.push(FrontendTask::SetTab(TabType::Home));
|
||||||
|
|
||||||
let id_label_time = state.get_widget_id("label_time")?;
|
let id_label_time = state.get_widget_id("label_time")?;
|
||||||
let id_rect_content = state.get_widget_id("rect_content")?;
|
let id_rect_content = state.get_widget_id("rect_content")?;
|
||||||
@@ -129,7 +143,12 @@ impl Frontend {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn update(&mut self, rc_this: &RcFrontend, width: f32, height: f32, timestep_alpha: f32) -> anyhow::Result<()> {
|
pub fn update(&mut self, rc_this: &RcFrontend, width: f32, height: f32, timestep_alpha: f32) -> anyhow::Result<()> {
|
||||||
while let Some(task) = self.tasks.pop_front() {
|
let mut tasks = {
|
||||||
|
let mut tasks = self.tasks.0.borrow_mut();
|
||||||
|
std::mem::take(&mut *tasks)
|
||||||
|
};
|
||||||
|
|
||||||
|
while let Some(task) = tasks.pop_front() {
|
||||||
self.process_task(rc_this, task)?;
|
self.process_task(rc_this, task)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,11 +201,19 @@ impl Frontend {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mount_popup(&mut self) -> anyhow::Result<()> {
|
fn mount_popup(&mut self, params: MountPopupParams) -> anyhow::Result<()> {
|
||||||
let mut layout = self.layout.borrow_mut();
|
let mut layout = self.layout.borrow_mut();
|
||||||
|
self
|
||||||
|
.popup_manager
|
||||||
|
.mount_popup(self.globals.clone(), &mut layout, self.tasks.clone(), params)?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
self.popup_manager.push_popup(self.globals.clone(), &mut layout)?;
|
fn refresh_popup_manager(&mut self) -> anyhow::Result<()> {
|
||||||
|
let mut layout = self.layout.borrow_mut();
|
||||||
|
let mut c = layout.start_common();
|
||||||
|
self.popup_manager.refresh(c.common().alterables);
|
||||||
|
c.finish()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,16 +244,13 @@ impl Frontend {
|
|||||||
&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) -> anyhow::Result<()> {
|
fn process_task(&mut self, rc_this: &RcFrontend, task: FrontendTask) -> anyhow::Result<()> {
|
||||||
match task {
|
match task {
|
||||||
FrontendTask::SetTab(tab_type) => self.set_tab(tab_type, rc_this)?,
|
FrontendTask::SetTab(tab_type) => self.set_tab(tab_type, rc_this)?,
|
||||||
FrontendTask::RefreshClock => self.update_time()?,
|
FrontendTask::RefreshClock => self.update_time()?,
|
||||||
FrontendTask::RefreshBackground => self.update_background()?,
|
FrontendTask::RefreshBackground => self.update_background()?,
|
||||||
FrontendTask::MountPopup => self.mount_popup()?,
|
FrontendTask::MountPopup(params) => self.mount_popup(params)?,
|
||||||
|
FrontendTask::RefreshPopupManager => self.refresh_popup_manager()?,
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,22 +1,34 @@
|
|||||||
use std::{collections::HashMap, rc::Rc};
|
use std::{cell::RefCell, collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
use wgui::{
|
use wgui::{
|
||||||
assets::AssetPath,
|
assets::AssetPath,
|
||||||
components::button::ComponentButton,
|
components::button::{ButtonClickCallback, ComponentButton},
|
||||||
|
globals::WguiGlobals,
|
||||||
|
i18n::Translation,
|
||||||
layout::WidgetPair,
|
layout::WidgetPair,
|
||||||
parser::{Fetchable, ParseDocumentParams, ParserData, ParserState},
|
parser::{Fetchable, ParseDocumentParams, ParserData, ParserState},
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
frontend::FrontendTask,
|
frontend::{FrontendTask, RcFrontend},
|
||||||
tab::{Tab, TabParams, TabType},
|
tab::{Tab, TabParams, TabType},
|
||||||
util::{self, desktop_finder::DesktopEntry},
|
util::{
|
||||||
views,
|
self,
|
||||||
|
desktop_finder::DesktopEntry,
|
||||||
|
popup_manager::{MountPopupParams, PopupHandle},
|
||||||
|
},
|
||||||
|
views::{self, app_launcher},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
launcher: Option<(PopupHandle, views::app_launcher::View)>,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TabApps {
|
pub struct TabApps {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub state: ParserState,
|
pub parser_state: ParserState,
|
||||||
|
|
||||||
|
state: Rc<RefCell<State>>,
|
||||||
|
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
entries: Vec<DesktopEntry>,
|
entries: Vec<DesktopEntry>,
|
||||||
@@ -35,6 +47,40 @@ struct AppList {
|
|||||||
data: Vec<ParserData>,
|
data: Vec<ParserData>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// called after the user clicks any desktop entry
|
||||||
|
fn on_app_click(
|
||||||
|
frontend: RcFrontend,
|
||||||
|
globals: WguiGlobals,
|
||||||
|
entry: DesktopEntry,
|
||||||
|
state: Rc<RefCell<State>>,
|
||||||
|
) -> ButtonClickCallback {
|
||||||
|
Box::new(move |_common, _evt| {
|
||||||
|
frontend
|
||||||
|
.borrow_mut()
|
||||||
|
.tasks
|
||||||
|
.push(FrontendTask::MountPopup(MountPopupParams {
|
||||||
|
title: Translation::from_raw_text(&entry.app_name),
|
||||||
|
on_content: {
|
||||||
|
let state = state.clone();
|
||||||
|
let entry = entry.clone();
|
||||||
|
let globals = globals.clone();
|
||||||
|
Box::new(move |data| {
|
||||||
|
let view = app_launcher::View::new(app_launcher::Params {
|
||||||
|
entry: entry.clone(),
|
||||||
|
globals: globals.clone(),
|
||||||
|
layout: data.layout,
|
||||||
|
parent_id: data.id_content,
|
||||||
|
})?;
|
||||||
|
|
||||||
|
state.borrow_mut().launcher = Some((data.handle, view));
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
impl TabApps {
|
impl TabApps {
|
||||||
pub fn new(mut tab_params: TabParams) -> anyhow::Result<Self> {
|
pub fn new(mut tab_params: TabParams) -> anyhow::Result<Self> {
|
||||||
let doc_params = &ParseDocumentParams {
|
let doc_params = &ParseDocumentParams {
|
||||||
@@ -43,21 +89,39 @@ impl TabApps {
|
|||||||
extra: Default::default(),
|
extra: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut state = wgui::parser::parse_from_assets(doc_params, tab_params.layout, tab_params.parent_id)?;
|
|
||||||
|
|
||||||
gtk::init()?;
|
gtk::init()?;
|
||||||
|
|
||||||
let entries = util::desktop_finder::find_entries()?;
|
let entries = util::desktop_finder::find_entries()?;
|
||||||
|
|
||||||
let app_list_parent = state.fetch_widget(&tab_params.layout.state, "app_list_parent")?;
|
let frontend = tab_params.frontend.clone();
|
||||||
|
let globals = tab_params.globals.clone();
|
||||||
|
|
||||||
|
let state = Rc::new(RefCell::new(State { launcher: None }));
|
||||||
|
|
||||||
|
let mut parser_state = wgui::parser::parse_from_assets(doc_params, tab_params.layout, tab_params.parent_id)?;
|
||||||
|
let app_list_parent = parser_state.fetch_widget(&tab_params.layout.state, "app_list_parent")?;
|
||||||
let mut app_list = AppList::default();
|
let mut app_list = AppList::default();
|
||||||
app_list.mount_entries(&entries, &mut state, doc_params, &mut tab_params, &app_list_parent)?;
|
app_list.mount_entries(
|
||||||
|
&entries,
|
||||||
|
&mut parser_state,
|
||||||
|
doc_params,
|
||||||
|
&mut tab_params,
|
||||||
|
&app_list_parent,
|
||||||
|
|button, entry| {
|
||||||
|
// Set up the click handler for the app button
|
||||||
|
button.on_click(on_app_click(
|
||||||
|
frontend.clone(),
|
||||||
|
globals.clone(),
|
||||||
|
entry.clone(),
|
||||||
|
state.clone(),
|
||||||
|
));
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
app_list,
|
app_list,
|
||||||
state,
|
parser_state,
|
||||||
entries,
|
entries,
|
||||||
|
state,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,7 +134,7 @@ impl AppList {
|
|||||||
params: &mut TabParams,
|
params: &mut TabParams,
|
||||||
list_parent: &WidgetPair,
|
list_parent: &WidgetPair,
|
||||||
entry: &DesktopEntry,
|
entry: &DesktopEntry,
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<Rc<ComponentButton>> {
|
||||||
let mut template_params = HashMap::new();
|
let mut template_params = HashMap::new();
|
||||||
|
|
||||||
// entry icon
|
// entry icon
|
||||||
@@ -95,20 +159,7 @@ impl AppList {
|
|||||||
template_params.insert(Rc::from("name"), Rc::from(entry.app_name.as_str()));
|
template_params.insert(Rc::from("name"), Rc::from(entry.app_name.as_str()));
|
||||||
|
|
||||||
let data = parser_state.parse_template(doc_params, "AppEntry", params.layout, list_parent.id, template_params)?;
|
let data = parser_state.parse_template(doc_params, "AppEntry", params.layout, list_parent.id, template_params)?;
|
||||||
|
data.fetch_component_as::<ComponentButton>("button")
|
||||||
let button = data.fetch_component_as::<ComponentButton>("button")?;
|
|
||||||
|
|
||||||
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);
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn mount_entries(
|
fn mount_entries(
|
||||||
@@ -118,9 +169,11 @@ impl AppList {
|
|||||||
doc_params: &ParseDocumentParams,
|
doc_params: &ParseDocumentParams,
|
||||||
params: &mut TabParams,
|
params: &mut TabParams,
|
||||||
list_parent: &WidgetPair,
|
list_parent: &WidgetPair,
|
||||||
|
on_button: impl Fn(Rc<ComponentButton>, &DesktopEntry),
|
||||||
) -> anyhow::Result<()> {
|
) -> anyhow::Result<()> {
|
||||||
for entry in entries {
|
for entry in entries {
|
||||||
self.mount_entry(parser_state, doc_params, params, list_parent, entry)?;
|
let button = self.mount_entry(parser_state, doc_params, params, list_parent, entry)?;
|
||||||
|
on_button(button, entry);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,7 +43,7 @@ impl TabType {
|
|||||||
pub fn register_button(this_rc: RcFrontend, btn: &Rc<ComponentButton>, tab: TabType) {
|
pub fn register_button(this_rc: RcFrontend, btn: &Rc<ComponentButton>, tab: TabType) {
|
||||||
btn.on_click({
|
btn.on_click({
|
||||||
Box::new(move |_common, _evt| {
|
Box::new(move |_common, _evt| {
|
||||||
this_rc.borrow_mut().push_task(FrontendTask::SetTab(tab));
|
this_rc.borrow_mut().tasks.push(FrontendTask::SetTab(tab));
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ impl TabSettings {
|
|||||||
state.data.fetch_component_as::<ComponentCheckbox>("cb_am_pm_clock")?,
|
state.data.fetch_component_as::<ComponentCheckbox>("cb_am_pm_clock")?,
|
||||||
|settings| &mut settings.general.am_pm_clock,
|
|settings| &mut settings.general.am_pm_clock,
|
||||||
Some(|frontend, _| {
|
Some(|frontend, _| {
|
||||||
frontend.push_task(FrontendTask::RefreshClock);
|
frontend.tasks.push(FrontendTask::RefreshClock);
|
||||||
}),
|
}),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
@@ -85,7 +85,7 @@ impl TabSettings {
|
|||||||
.fetch_component_as::<ComponentCheckbox>("cb_opaque_background")?,
|
.fetch_component_as::<ComponentCheckbox>("cb_opaque_background")?,
|
||||||
|settings| &mut settings.general.opaque_background,
|
|settings| &mut settings.general.opaque_background,
|
||||||
Some(|frontend, _| {
|
Some(|frontend, _| {
|
||||||
frontend.push_task(FrontendTask::RefreshBackground);
|
frontend.tasks.push(FrontendTask::RefreshBackground);
|
||||||
}),
|
}),
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
|
|||||||
@@ -8,25 +8,47 @@ use wgui::{
|
|||||||
components::button::ComponentButton,
|
components::button::ComponentButton,
|
||||||
event::{EventAlterables, StyleSetRequest},
|
event::{EventAlterables, StyleSetRequest},
|
||||||
globals::WguiGlobals,
|
globals::WguiGlobals,
|
||||||
layout::{Layout, LayoutTask, WidgetID},
|
i18n::Translation,
|
||||||
|
layout::{Layout, LayoutTask, LayoutTasks, WidgetID},
|
||||||
parser::{Fetchable, ParseDocumentParams, ParserState},
|
parser::{Fetchable, ParseDocumentParams, ParserState},
|
||||||
taffy::Display,
|
taffy::Display,
|
||||||
|
widget::label::WidgetLabel,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::frontend::{FrontendTask, FrontendTasks};
|
||||||
|
|
||||||
pub struct PopupManagerParams<'a> {
|
pub struct PopupManagerParams<'a> {
|
||||||
pub globals: WguiGlobals,
|
pub globals: WguiGlobals,
|
||||||
pub layout: &'a mut Layout,
|
pub layout: &'a mut Layout,
|
||||||
pub parent_id: WidgetID,
|
pub parent_id: WidgetID,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MountedPopup {
|
pub struct State {
|
||||||
#[allow(dead_code)]
|
popup_stack: Vec<Weak<RefCell<MountedPopupState>>>,
|
||||||
state: ParserState,
|
|
||||||
id_root: WidgetID,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct State {
|
pub struct MountedPopup {
|
||||||
popup_stack: Vec<MountedPopup>,
|
#[allow(dead_code)]
|
||||||
|
state: ParserState,
|
||||||
|
id_root: WidgetID, // decorations of a popup
|
||||||
|
pub id_content: WidgetID, // content of a popup
|
||||||
|
layout_tasks: LayoutTasks,
|
||||||
|
frontend_tasks: FrontendTasks,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MountedPopupState {
|
||||||
|
mounted_popup: Option<MountedPopup>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PopupHandle {
|
||||||
|
state: Rc<RefCell<MountedPopupState>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PopupHandle {
|
||||||
|
pub fn close(&self) {
|
||||||
|
self.state.borrow_mut().mounted_popup = None; // Drop will be called
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PopupManager {
|
pub struct PopupManager {
|
||||||
@@ -35,16 +57,41 @@ pub struct PopupManager {
|
|||||||
parent_id: WidgetID,
|
parent_id: WidgetID,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PushPopupResult {
|
pub struct PopupContentFuncData<'a> {
|
||||||
|
pub layout: &'a mut Layout,
|
||||||
|
pub handle: PopupHandle,
|
||||||
pub id_content: WidgetID,
|
pub id_content: WidgetID,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct MountPopupParams {
|
||||||
|
pub title: Translation,
|
||||||
|
pub on_content: Box<dyn Fn(PopupContentFuncData) -> anyhow::Result<()>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Drop for MountedPopup {
|
||||||
|
fn drop(&mut self) {
|
||||||
|
self.layout_tasks.push(LayoutTask::RemoveWidget(self.id_root));
|
||||||
|
self.frontend_tasks.push(FrontendTask::RefreshPopupManager);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl State {
|
impl State {
|
||||||
fn refresh_stack(&mut self, alterables: &mut EventAlterables) {
|
fn refresh_stack(&mut self, alterables: &mut EventAlterables) {
|
||||||
// show only the topmost popup
|
// show only the topmost popup
|
||||||
|
self.popup_stack.retain(|weak| {
|
||||||
|
let Some(popup) = weak.upgrade() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
popup.borrow_mut().mounted_popup.is_some()
|
||||||
|
});
|
||||||
|
|
||||||
for (idx, popup) in self.popup_stack.iter().enumerate() {
|
for (idx, popup) in self.popup_stack.iter().enumerate() {
|
||||||
|
let popup = popup.upgrade().unwrap(); // safe
|
||||||
|
let popup = popup.borrow_mut();
|
||||||
|
let mounted_popup = popup.mounted_popup.as_ref().unwrap(); // safe;
|
||||||
|
|
||||||
alterables.set_style(
|
alterables.set_style(
|
||||||
popup.id_root,
|
mounted_popup.id_root,
|
||||||
StyleSetRequest::Display(if idx == self.popup_stack.len() - 1 {
|
StyleSetRequest::Display(if idx == self.popup_stack.len() - 1 {
|
||||||
Display::Flex
|
Display::Flex
|
||||||
} else {
|
} else {
|
||||||
@@ -53,15 +100,6 @@ impl State {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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 {
|
impl PopupManager {
|
||||||
@@ -75,9 +113,22 @@ impl PopupManager {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn push_popup(&mut self, globals: WguiGlobals, layout: &mut Layout) -> anyhow::Result<PushPopupResult> {
|
pub fn refresh(&self, alterables: &mut EventAlterables) {
|
||||||
|
let mut state = self.state.borrow_mut();
|
||||||
|
state.refresh_stack(alterables);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Mount a new popup on top of the existing popup stack.
|
||||||
|
/// Only the topmost popup is visible.
|
||||||
|
pub fn mount_popup(
|
||||||
|
&mut self,
|
||||||
|
globals: WguiGlobals,
|
||||||
|
layout: &mut Layout,
|
||||||
|
frontend_tasks: FrontendTasks,
|
||||||
|
params: MountPopupParams,
|
||||||
|
) -> anyhow::Result<()> {
|
||||||
let doc_params = &ParseDocumentParams {
|
let doc_params = &ParseDocumentParams {
|
||||||
globals,
|
globals: globals.clone(),
|
||||||
path: AssetPath::BuiltIn("gui/view/popup_window.xml"),
|
path: AssetPath::BuiltIn("gui/view/popup_window.xml"),
|
||||||
extra: Default::default(),
|
extra: Default::default(),
|
||||||
};
|
};
|
||||||
@@ -86,25 +137,51 @@ impl PopupManager {
|
|||||||
let id_root = state.get_widget_id("root")?;
|
let id_root = state.get_widget_id("root")?;
|
||||||
let id_content = state.get_widget_id("content")?;
|
let id_content = state.get_widget_id("content")?;
|
||||||
|
|
||||||
|
{
|
||||||
|
let mut label_title = state.fetch_widget_as::<WidgetLabel>(&layout.state, "popup_title")?;
|
||||||
|
label_title.set_text_simple(&mut globals.get(), params.title);
|
||||||
|
}
|
||||||
|
|
||||||
let but_back = state.fetch_component_as::<ComponentButton>("but_back")?;
|
let but_back = state.fetch_component_as::<ComponentButton>("but_back")?;
|
||||||
|
|
||||||
|
let mounted_popup = MountedPopup {
|
||||||
|
state,
|
||||||
|
id_content,
|
||||||
|
id_root,
|
||||||
|
layout_tasks: layout.tasks.clone(),
|
||||||
|
frontend_tasks: frontend_tasks.clone(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mounted_popup_state = MountedPopupState {
|
||||||
|
mounted_popup: Some(mounted_popup),
|
||||||
|
};
|
||||||
|
|
||||||
|
let popup_handle = PopupHandle {
|
||||||
|
state: Rc::new(RefCell::new(mounted_popup_state)),
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut state = self.state.borrow_mut();
|
||||||
|
state.popup_stack.push(Rc::downgrade(&popup_handle.state));
|
||||||
|
|
||||||
but_back.on_click({
|
but_back.on_click({
|
||||||
let state = self.state.clone();
|
let popup_handle = Rc::downgrade(&popup_handle.state);
|
||||||
Box::new(move |common, _evt| {
|
Box::new(move |_common, _evt| {
|
||||||
state.borrow_mut().pop_popup(common.alterables);
|
if let Some(popup_handle) = popup_handle.upgrade() {
|
||||||
|
popup_handle.borrow_mut().mounted_popup = None; // will call Drop
|
||||||
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let mounted_popup = MountedPopup { state, id_root };
|
frontend_tasks.push(FrontendTask::RefreshPopupManager);
|
||||||
|
|
||||||
let mut state = self.state.borrow_mut();
|
// mount user-set popup content
|
||||||
state.popup_stack.push(mounted_popup);
|
(*params.on_content)(PopupContentFuncData {
|
||||||
|
layout,
|
||||||
|
handle: popup_handle.clone(),
|
||||||
|
id_content,
|
||||||
|
})?;
|
||||||
|
|
||||||
let mut c = layout.start_common();
|
Ok(())
|
||||||
state.refresh_stack(c.common().alterables);
|
|
||||||
c.finish()?;
|
|
||||||
|
|
||||||
Ok(PushPopupResult { id_content })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +1,12 @@
|
|||||||
|
use std::{collections::HashMap, rc::Rc};
|
||||||
|
|
||||||
use wgui::{
|
use wgui::{
|
||||||
assets::AssetPath,
|
assets::AssetPath,
|
||||||
globals::WguiGlobals,
|
globals::WguiGlobals,
|
||||||
|
i18n::Translation,
|
||||||
layout::{Layout, WidgetID},
|
layout::{Layout, WidgetID},
|
||||||
parser::{ParseDocumentParams, ParserState},
|
parser::{Fetchable, ParseDocumentParams, ParserState},
|
||||||
|
widget::label::WidgetLabel,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::util::desktop_finder::DesktopEntry;
|
use crate::util::desktop_finder::DesktopEntry;
|
||||||
@@ -10,8 +14,7 @@ use crate::util::desktop_finder::DesktopEntry;
|
|||||||
pub struct View {
|
pub struct View {
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub state: ParserState,
|
pub state: ParserState,
|
||||||
|
//entry: DesktopEntry,
|
||||||
entry: DesktopEntry,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Params<'a> {
|
pub struct Params<'a> {
|
||||||
@@ -30,9 +33,30 @@ impl View {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut state = wgui::parser::parse_from_assets(doc_params, params.layout, params.parent_id)?;
|
let mut state = wgui::parser::parse_from_assets(doc_params, params.layout, params.parent_id)?;
|
||||||
|
let id_icon_parent = state.get_widget_id("icon_parent")?;
|
||||||
|
|
||||||
|
// app icon
|
||||||
|
if let Some(icon_path) = ¶ms.entry.icon_path {
|
||||||
|
let mut template_params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||||
|
template_params.insert("path".into(), icon_path.as_str().into());
|
||||||
|
state.instantiate_template(
|
||||||
|
doc_params,
|
||||||
|
"ApplicationIcon",
|
||||||
|
params.layout,
|
||||||
|
id_icon_parent,
|
||||||
|
template_params,
|
||||||
|
)?;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut label_title = state.fetch_widget_as::<WidgetLabel>(¶ms.layout.state, "label_title")?;
|
||||||
|
|
||||||
|
label_title.set_text_simple(
|
||||||
|
&mut params.globals.get(),
|
||||||
|
Translation::from_raw_text(¶ms.entry.app_name),
|
||||||
|
);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
entry: params.entry,
|
//entry: params.entry,
|
||||||
state,
|
state,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -237,7 +237,6 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
|||||||
// force-override style
|
// force-override style
|
||||||
style.flex_wrap = taffy::FlexWrap::NoWrap;
|
style.flex_wrap = taffy::FlexWrap::NoWrap;
|
||||||
style.align_items = Some(AlignItems::Center);
|
style.align_items = Some(AlignItems::Center);
|
||||||
style.justify_content = Some(JustifyContent::Center);
|
|
||||||
|
|
||||||
// make checkbox interaction box larger by setting padding and negative margin
|
// make checkbox interaction box larger by setting padding and negative margin
|
||||||
style.padding = taffy::Rect {
|
style.padding = taffy::Rect {
|
||||||
|
|||||||
Reference in New Issue
Block a user