dash-frontend: Top panel title

This commit is contained in:
Aleksander
2026-01-27 18:43:34 +01:00
parent 2c60196ead
commit 19083cfd83
12 changed files with 87 additions and 39 deletions

View File

@@ -3,6 +3,7 @@
<theme>
<var key="side_size" value="48" />
<var key="top_size" value="40" />
<var key="side_sprite_size" value="26" />
<var key="side_button_size" value="48" />
</theme>
@@ -25,13 +26,6 @@
</template>
<elements>
<div position="absolute" width="100%" justify_content="space_between">
<div />
<div padding="6">
<Button id="btn_close" sprite_src_builtin="dashboard/close.svg" color="#000000" border="2" border_color="~color_faded" />
</div>
</div>
<!-- left/right separator (menu and rest) -->
<div flex_direction="row" gap="8" width="100%" height="100%" padding="4" interactable="0">
<!-- LEFT MENU -->
@@ -60,15 +54,29 @@
</rectangle>
</div>
<!-- REST -->
<!-- content/bottom panel separator -->
<!-- top/content/bottom panel separator -->
<div
flex_direction="column"
gap="8"
width="100%"
height="100%"
overflow_x="scroll">
<!-- CONTENT -->
<!-- TOP PANEL -->
<div position="relative" width="100%" height="~top_size" min_height="~top_size" max_height="~top_size" justify_content="end">
<!-- Title bar -->
<div width="100%" align_items="center" justify_content="center">
<rectangle min_width="300" height="100%" consume_mouse_events="1" round="100%" color="~color_top_panel" align_items="center" justify_content="center" gap="8">
<sprite id="sprite_titlebar_icon" width="28" height="28" />
<label id="label_titlebar_title" weight="bold" size="16" text="Title" />
</rectangle>
</div>
<!-- Close button -->
<Button position="absolute" id="btn_close" color="~color_top_panel" round="100%" width="~top_size" height="~top_size" tooltip="CLOSE_WINDOW" tooltip_side="left">
<sprite src_builtin="dashboard/close.svg" width="32" height="32" />
</Button>
</div>
<!-- CONTENT -->
<!-- color and color2 alpha will be modified at runtime -->
<rectangle
id="rect_content"

View File

@@ -1,5 +1,4 @@
<layout>
<include src="t_tab_title.xml" />
<include src="../theme.xml" />
<template name="AppEntry">
@@ -25,7 +24,6 @@
</template>
<elements>
<TabTitle translation="APPLICATIONS" icon="dashboard/apps.svg" />
<!-- placeholders for now -->
<!--
<div gap="4" align_items="center">

View File

@@ -1,9 +1,7 @@
<layout>
<include src="t_tab_title.xml" />
<include src="../t_group_box.xml" />
<elements>
<TabTitle translation="GAMES" icon="dashboard/games.svg" />
<rectangle macro="group_box" id="running_games_list_parent" align_self="center" />
<div id="game_list_parent" align_items="center" />
</elements>

View File

@@ -1,8 +1,6 @@
<layout>
<include src="t_tab_title.xml" />
<include src="../t_group_box.xml" />
<!-- key: str, value: str -->
<template name="BoolFlag">
<div flex_direction="row" gap="4">
@@ -27,7 +25,6 @@
</template>
<elements>
<TabTitle translation="MONADO_RUNTIME" icon="dashboard/monado.svg" />
<label translation="DISPLAY_BRIGHTNESS" />
<Slider id="slider_brightness" width="300" height="24" min_value="0" max_value="140" />

View File

@@ -1,5 +1,4 @@
<layout>
<include src="t_tab_title.xml" />
<include src="../t_group_box.xml" />
<include src="../t_dropdown_button.xml" />
@@ -46,7 +45,6 @@
</template>
<elements>
<TabTitle translation="SETTINGS" icon="dashboard/settings.svg" />
<div gap="4">
<Tabs id="tabs">
<Tab name="look_and_feel" translation="APP_SETTINGS.LOOK_AND_FEEL" sprite_src_builtin="dashboard/palette.svg" />

View File

@@ -1,9 +0,0 @@
<layout>
<!-- translation, icon -->
<template name="TabTitle">
<div gap="8" align_items="center">
<sprite src_builtin="${icon}" width="24" height="24" />
<label translation="${translation}" size="18" weight="bold" />
</div>
</template>
</layout>

View File

@@ -2,6 +2,7 @@
<theme>
<var key="color_menu_dark" value="#0A0A0ACC" />
<var key="color_accent" value="#008cff" />
<var key="color_top_panel" value="#00000099" />
<var key="color_bottom_panel" value="#141e28" />
</theme>
</layout>

View File

@@ -1,5 +1,6 @@
use std::{path::PathBuf, rc::Rc};
use anyhow::Context;
use chrono::Timelike;
use glam::Vec2;
use wgui::{
@@ -10,8 +11,9 @@ use wgui::{
i18n::Translation,
layout::{Layout, LayoutParams, LayoutUpdateParams, LayoutUpdateResult, WidgetID},
parser::{Fetchable, ParseDocumentParams, ParserState},
renderer_vk::text::custom_glyph::CustomGlyphData,
task::Tasks,
widget::{label::WidgetLabel, rectangle::WidgetRectangle},
widget::{label::WidgetLabel, rectangle::WidgetRectangle, sprite::WidgetSprite},
windowing::window::{WguiWindow, WguiWindowParams, WguiWindowParamsExtra, WguiWindowPlacement},
};
use wlx_common::{
@@ -22,7 +24,7 @@ use wlx_common::{
use crate::{
assets,
tab::{apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, settings::TabSettings, Tab, TabType},
tab::{Tab, TabType, apps::TabApps, games::TabGames, home::TabHome, monado::TabMonado, settings::TabSettings},
util::{
popup_manager::{MountPopupParams, PopupManager, PopupManagerParams},
toast_manager::ToastManager,
@@ -32,8 +34,10 @@ use crate::{
};
pub struct FrontendWidgets {
pub id_label_time: WidgetID,
pub id_rect_content: WidgetID,
id_label_time: WidgetID,
id_rect_content: WidgetID,
id_sprite_titlebar_icon: WidgetID,
id_label_titlebar_title: WidgetID,
}
pub type FrontendTasks = Tasks<FrontendTask>;
@@ -148,6 +152,8 @@ impl<T: 'static> Frontend<T> {
let id_label_time = state.get_widget_id("label_time")?;
let id_rect_content = state.get_widget_id("rect_content")?;
let id_sprite_titlebar_icon = state.get_widget_id("sprite_titlebar_icon")?;
let id_label_titlebar_title = state.get_widget_id("label_titlebar_title")?;
let timestep = Timestep::new(60.0);
@@ -161,6 +167,8 @@ impl<T: 'static> Frontend<T> {
widgets: FrontendWidgets {
id_label_time,
id_rect_content,
id_sprite_titlebar_icon,
id_label_titlebar_title,
},
timestep,
interface: params.interface,
@@ -283,9 +291,10 @@ impl<T: 'static> Frontend<T> {
let mut common = c.common();
{
let Some(mut label) = common.state.widgets.get_as::<WidgetLabel>(self.widgets.id_label_time) else {
anyhow::bail!("");
};
let mut label = common
.state
.widgets
.cast_as::<WidgetLabel>(self.widgets.id_label_time)?;
let now = chrono::Local::now();
let hours = now.hour();
@@ -370,11 +379,48 @@ impl<T: 'static> Frontend<T> {
Ok(())
}
fn set_tab_title(&mut self, translation: &str, icon: &str) -> anyhow::Result<()> {
let mut c = self.layout.start_common();
let mut common = c.common();
{
let mut label = common
.state
.widgets
.cast_as::<WidgetLabel>(self.widgets.id_label_titlebar_title)?;
label.set_text(&mut common, Translation::from_translation_key(translation));
}
{
let mut sprite = common
.state
.widgets
.cast_as::<WidgetSprite>(self.widgets.id_sprite_titlebar_icon)?;
sprite.set_content(
&mut common,
Some(CustomGlyphData::from_assets(&self.globals, AssetPath::BuiltIn(icon))?),
);
}
c.finish()?;
Ok(())
}
fn set_tab(&mut self, data: &mut T, tab_type: TabType) -> anyhow::Result<()> {
log::info!("Setting tab to {tab_type:?}");
let widget_content = self.state.fetch_widget(&self.layout.state, "content")?;
self.layout.remove_children(widget_content.id);
let (tab_translation, icon_path) = match tab_type {
TabType::Home => ("HOME_SCREEN", "dashboard/home.svg"),
TabType::Apps => ("APPLICATIONS", "dashboard/apps.svg"),
TabType::Games => ("GAMES", "dashboard/games.svg"),
TabType::Monado => ("MONADO_RUNTIME", "dashboard/monado.svg"),
TabType::Settings => ("SETTINGS", "dashboard/settings.svg"),
};
self.set_tab_title(tab_translation, icon_path)?;
let tab: Box<dyn Tab<T>> = match tab_type {
TabType::Home => Box::new(TabHome::new(self, widget_content.id, data)?),
TabType::Apps => Box::new(TabApps::new(self, widget_content.id, data)?),

View File

@@ -55,8 +55,7 @@ fn button_click_callback(
) -> ButtonClickCallback {
Rc::new(move |common, _e| {
label
.get_as::<WidgetLabel>()
.unwrap()
.cast::<WidgetLabel>()?
.set_text(common, Translation::from_raw_text(text));
button.try_cast::<ComponentButton>()?.set_text(
@@ -169,7 +168,7 @@ impl TestbedGeneric {
let cb_first = parser_state.fetch_component_as::<ComponentCheckbox>("cb_first")?;
let label = label_cur_option.widget.clone();
cb_first.on_toggle(Box::new(move |common, e| {
let mut widget = label.get_as::<WidgetLabel>().unwrap();
let mut widget = label.cast::<WidgetLabel>()?;
let text = format!("checkbox toggle: {}", e.checked);
widget.set_text(common, Translation::from_raw_text(&text));
Ok(())

View File

@@ -103,8 +103,7 @@ pub(super) fn setup_custom_label<S: 'static>(
layout
.state
.widgets
.get_as::<WidgetLabel>(attribs.widget_id)
.unwrap()
.cast_as::<WidgetLabel>(attribs.widget_id)?
.set_text_simple(&mut globals, Translation::from_raw_text(pretty_tz));
// does not need to be dynamic

View File

@@ -75,6 +75,10 @@ impl WidgetMap {
self.0.get(handle)?.get_as::<T>()
}
pub fn cast_as<T: 'static>(&self, handle: WidgetID) -> anyhow::Result<RefMut<'_, T>> {
self.get_as(handle).context("Widget cast failed")
}
pub fn get(&self, handle: WidgetID) -> Option<&Widget> {
self.0.get(handle)
}

View File

@@ -1,3 +1,4 @@
use anyhow::Context;
use glam::Vec2;
use taffy::{NodeId, TaffyTree};
@@ -248,10 +249,18 @@ impl dyn WidgetObj {
any.downcast_ref::<T>()
}
pub fn cast<T: 'static>(&self) -> anyhow::Result<&T> {
self.get_as().context("cast failed")
}
pub fn get_as_mut<T: 'static>(&mut self) -> Option<&mut T> {
let any = self.as_any_mut();
any.downcast_mut::<T>()
}
pub fn cast_mut<T: 'static>(&mut self) -> anyhow::Result<&mut T> {
self.get_as_mut().context("cast failed")
}
}
struct InvokeData<'a, 'b, U1: 'static, U2: 'static> {