Tabbed settings (Closes #355)
This commit is contained in:
@@ -28,7 +28,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template name="DangerButton">
|
<template name="DangerButton">
|
||||||
<Button id="${id}" color="#AA3333" height="32" tooltip="${translation}_HELP" padding="4" gap="8" >
|
<Button id="${id}" color="#AA3333" height="32" tooltip="${translation}_HELP" padding="4" gap="8">
|
||||||
<sprite src_builtin="${icon}" height="24" width="24" />
|
<sprite src_builtin="${icon}" height="24" width="24" />
|
||||||
<label align="left" translation="${translation}" weight="bold" min_width="200" />
|
<label align="left" translation="${translation}" weight="bold" min_width="200" />
|
||||||
</Button>
|
</Button>
|
||||||
@@ -36,17 +36,27 @@
|
|||||||
|
|
||||||
<template name="AutostartApp">
|
<template name="AutostartApp">
|
||||||
<div id="${id}_root" flex_direction="row">
|
<div id="${id}_root" flex_direction="row">
|
||||||
<Button id="${id}" color="#AA3333" height="24" padding="4" margin_top="-2" margin_bottom="-2" >
|
<Button id="${id}" color="#AA3333" height="24" padding="4" margin_top="-2" margin_bottom="-2">
|
||||||
<sprite src_builtin="dashboard/close.svg" height="20" width="20" />
|
<sprite src_builtin="dashboard/close.svg" height="20" width="20" />
|
||||||
</Button>
|
</Button>
|
||||||
<div padding_left="8" >
|
<div padding_left="8">
|
||||||
<label align="left" text="${text}" weight="bold" overflow="hidden"/>
|
<label align="left" text="${text}" weight="bold" overflow="hidden" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<elements>
|
<elements>
|
||||||
<TabTitle translation="SETTINGS" icon="dashboard/settings.svg" />
|
<TabTitle translation="SETTINGS" icon="dashboard/settings.svg" />
|
||||||
<div flex_wrap="wrap" justify_content="stretch" gap="4" id="settings_root" />
|
<div gap="4">
|
||||||
|
<Tabs id="tabs">
|
||||||
|
<Tab name="look_and_feel" translation="APP_SETTINGS.LOOK_AND_FEEL" sprite_src_builtin="dashboard/palette.svg" />
|
||||||
|
<Tab name="features" translation="APP_SETTINGS.FEATURES" sprite_src_builtin="dashboard/options.svg" />
|
||||||
|
<Tab name="controls" translation="APP_SETTINGS.CONTROLS" sprite_src_builtin="dashboard/controller.svg" />
|
||||||
|
<Tab name="misc" translation="APP_SETTINGS.MISC" sprite_src_builtin="dashboard/blocks.svg" />
|
||||||
|
<Tab name="autostart_apps" translation="APP_SETTINGS.AUTOSTART_APPS" sprite_src_builtin="dashboard/apps.svg" />
|
||||||
|
<Tab name="troubleshooting" translation="APP_SETTINGS.TROUBLESHOOTING" sprite_src_builtin="dashboard/cpu.svg" />
|
||||||
|
</Tabs>
|
||||||
|
<div flex_wrap="wrap" justify_content="stretch" gap="4" id="settings_root" width="100%" />
|
||||||
|
</div>
|
||||||
</elements>
|
</elements>
|
||||||
</layout>
|
</layout>
|
||||||
@@ -81,7 +81,7 @@ fn on_app_click(
|
|||||||
state: Rc<RefCell<State>>,
|
state: Rc<RefCell<State>>,
|
||||||
tasks: Tasks<Task>,
|
tasks: Tasks<Task>,
|
||||||
) -> ButtonClickCallback {
|
) -> ButtonClickCallback {
|
||||||
Box::new(move |_common, _evt| {
|
Rc::new(move |_common, _evt| {
|
||||||
frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams {
|
frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams {
|
||||||
title: Translation::from_raw_text(&entry.app_name),
|
title: Translation::from_raw_text(&entry.app_name),
|
||||||
on_content: {
|
on_content: {
|
||||||
|
|||||||
@@ -4,8 +4,14 @@ use glam::Vec2;
|
|||||||
use strum::{AsRefStr, EnumProperty, EnumString, VariantArray};
|
use strum::{AsRefStr, EnumProperty, EnumString, VariantArray};
|
||||||
use wgui::{
|
use wgui::{
|
||||||
assets::AssetPath,
|
assets::AssetPath,
|
||||||
components::{button::ComponentButton, checkbox::ComponentCheckbox, slider::ComponentSlider},
|
components::{
|
||||||
|
button::{ButtonClickEvent, ComponentButton},
|
||||||
|
checkbox::ComponentCheckbox,
|
||||||
|
slider::ComponentSlider,
|
||||||
|
tabs::ComponentTabs,
|
||||||
|
},
|
||||||
event::{CallbackDataCommon, EventAlterables},
|
event::{CallbackDataCommon, EventAlterables},
|
||||||
|
globals::WguiGlobals,
|
||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::{Layout, WidgetID},
|
layout::{Layout, WidgetID},
|
||||||
log::LogErr,
|
log::LogErr,
|
||||||
@@ -21,6 +27,30 @@ use crate::{
|
|||||||
tab::{Tab, TabType},
|
tab::{Tab, TabType},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
enum TabNameEnum {
|
||||||
|
LookAndFeel,
|
||||||
|
Features,
|
||||||
|
Controls,
|
||||||
|
Misc,
|
||||||
|
AutostartApps,
|
||||||
|
Troubleshooting,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TabNameEnum {
|
||||||
|
fn from_string(s: &str) -> Option<Self> {
|
||||||
|
match s {
|
||||||
|
"look_and_feel" => Some(TabNameEnum::LookAndFeel),
|
||||||
|
"features" => Some(TabNameEnum::Features),
|
||||||
|
"controls" => Some(TabNameEnum::Controls),
|
||||||
|
"misc" => Some(TabNameEnum::Misc),
|
||||||
|
"autostart_apps" => Some(TabNameEnum::AutostartApps),
|
||||||
|
"troubleshooting" => Some(TabNameEnum::Troubleshooting),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
enum Task {
|
enum Task {
|
||||||
UpdateBool(SettingType, bool),
|
UpdateBool(SettingType, bool),
|
||||||
UpdateFloat(SettingType, f32),
|
UpdateFloat(SettingType, f32),
|
||||||
@@ -31,6 +61,7 @@ enum Task {
|
|||||||
DeleteAllConfigs,
|
DeleteAllConfigs,
|
||||||
RestartSoftware,
|
RestartSoftware,
|
||||||
RemoveAutostartApp(Rc<str>),
|
RemoveAutostartApp(Rc<str>),
|
||||||
|
SetTab(TabNameEnum),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct TabSettings<T> {
|
pub struct TabSettings<T> {
|
||||||
@@ -49,22 +80,33 @@ impl<T> Tab<T> for TabSettings<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn update(&mut self, frontend: &mut Frontend<T>, data: &mut T) -> anyhow::Result<()> {
|
fn update(&mut self, frontend: &mut Frontend<T>, data: &mut T) -> anyhow::Result<()> {
|
||||||
let config = frontend.interface.general_config(data);
|
|
||||||
let mut changed = false;
|
let mut changed = false;
|
||||||
for task in self.tasks.drain() {
|
for task in self.tasks.drain() {
|
||||||
match task {
|
match task {
|
||||||
|
Task::SetTab(tab) => {
|
||||||
|
self.set_tab(frontend, data, tab)?;
|
||||||
|
}
|
||||||
Task::UpdateBool(setting, n) => {
|
Task::UpdateBool(setting, n) => {
|
||||||
setting.get_frontend_task().map(|task| frontend.tasks.push(task));
|
if let Some(task) = setting.get_frontend_task() {
|
||||||
|
frontend.tasks.push(task)
|
||||||
|
}
|
||||||
|
let config = frontend.interface.general_config(data);
|
||||||
*setting.mut_bool(config) = n;
|
*setting.mut_bool(config) = n;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
Task::UpdateFloat(setting, n) => {
|
Task::UpdateFloat(setting, n) => {
|
||||||
setting.get_frontend_task().map(|task| frontend.tasks.push(task));
|
if let Some(task) = setting.get_frontend_task() {
|
||||||
|
frontend.tasks.push(task)
|
||||||
|
}
|
||||||
|
let config = frontend.interface.general_config(data);
|
||||||
*setting.mut_f32(config) = n;
|
*setting.mut_f32(config) = n;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
Task::UpdateInt(setting, n) => {
|
Task::UpdateInt(setting, n) => {
|
||||||
setting.get_frontend_task().map(|task| frontend.tasks.push(task));
|
if let Some(task) = setting.get_frontend_task() {
|
||||||
|
frontend.tasks.push(task)
|
||||||
|
}
|
||||||
|
let config = frontend.interface.general_config(data);
|
||||||
*setting.mut_i32(config) = n;
|
*setting.mut_i32(config) = n;
|
||||||
changed = true;
|
changed = true;
|
||||||
}
|
}
|
||||||
@@ -98,6 +140,7 @@ impl<T> Tab<T> for TabSettings<T> {
|
|||||||
self.state.get_widget_id(&format!("{button_id}_root")),
|
self.state.get_widget_id(&format!("{button_id}_root")),
|
||||||
) {
|
) {
|
||||||
self.app_button_ids.remove(idx);
|
self.app_button_ids.remove(idx);
|
||||||
|
let config = frontend.interface.general_config(data);
|
||||||
config.autostart_apps.remove(idx);
|
config.autostart_apps.remove(idx);
|
||||||
frontend.layout.remove_widget(widget);
|
frontend.layout.remove_widget(widget);
|
||||||
changed = true;
|
changed = true;
|
||||||
@@ -107,32 +150,32 @@ impl<T> Tab<T> for TabSettings<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Dropdown handling
|
// Dropdown handling
|
||||||
if let TickResult::Action(name) = self.context_menu.tick(&mut frontend.layout, &mut self.state)? {
|
if let TickResult::Action(name) = self.context_menu.tick(&mut frontend.layout, &mut self.state)?
|
||||||
if let (Some(setting), Some(id), Some(value), Some(text), Some(translated)) = {
|
&& let (Some(setting), Some(id), Some(value), Some(text), Some(translated)) = {
|
||||||
let mut s = name.splitn(5, ';');
|
let mut s = name.splitn(5, ';');
|
||||||
(s.next(), s.next(), s.next(), s.next(), s.next())
|
(s.next(), s.next(), s.next(), s.next(), s.next())
|
||||||
} {
|
} {
|
||||||
let mut label = self
|
let mut label = self
|
||||||
.state
|
.state
|
||||||
.fetch_widget_as::<WidgetLabel>(&frontend.layout.state, &format!("{id}_value"))?;
|
.fetch_widget_as::<WidgetLabel>(&frontend.layout.state, &format!("{id}_value"))?;
|
||||||
|
|
||||||
let mut alterables = EventAlterables::default();
|
let mut alterables = EventAlterables::default();
|
||||||
let mut common = CallbackDataCommon {
|
let mut common = CallbackDataCommon {
|
||||||
alterables: &mut alterables,
|
alterables: &mut alterables,
|
||||||
state: &frontend.layout.state,
|
state: &frontend.layout.state,
|
||||||
};
|
};
|
||||||
|
|
||||||
let translation = Translation {
|
let translation = Translation {
|
||||||
text: text.into(),
|
text: text.into(),
|
||||||
translated: translated == "1",
|
translated: translated == "1",
|
||||||
};
|
};
|
||||||
|
|
||||||
label.set_text(&mut common, translation);
|
label.set_text(&mut common, translation);
|
||||||
|
|
||||||
let setting = SettingType::from_str(setting).expect("Invalid Enum string");
|
let setting = SettingType::from_str(setting).expect("Invalid Enum string");
|
||||||
setting.set_enum(config, value);
|
let config = frontend.interface.general_config(data);
|
||||||
changed = true;
|
setting.set_enum(config, value);
|
||||||
}
|
changed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Notify overlays of the change
|
// Notify overlays of the change
|
||||||
@@ -184,7 +227,7 @@ enum SettingType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SettingType {
|
impl SettingType {
|
||||||
pub fn mut_bool<'a>(self, config: &'a mut GeneralConfig) -> &'a mut bool {
|
pub fn mut_bool(self, config: &mut GeneralConfig) -> &mut bool {
|
||||||
match self {
|
match self {
|
||||||
Self::InvertScrollDirectionX => &mut config.invert_scroll_direction_x,
|
Self::InvertScrollDirectionX => &mut config.invert_scroll_direction_x,
|
||||||
Self::InvertScrollDirectionY => &mut config.invert_scroll_direction_y,
|
Self::InvertScrollDirectionY => &mut config.invert_scroll_direction_y,
|
||||||
@@ -213,7 +256,7 @@ impl SettingType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mut_f32<'a>(self, config: &'a mut GeneralConfig) -> &'a mut f32 {
|
pub fn mut_f32(self, config: &mut GeneralConfig) -> &mut f32 {
|
||||||
match self {
|
match self {
|
||||||
Self::AnimationSpeed => &mut config.animation_speed,
|
Self::AnimationSpeed => &mut config.animation_speed,
|
||||||
Self::RoundMultiplier => &mut config.round_multiplier,
|
Self::RoundMultiplier => &mut config.round_multiplier,
|
||||||
@@ -227,14 +270,14 @@ impl SettingType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn mut_i32<'a>(self, config: &'a mut GeneralConfig) -> &'a mut i32 {
|
pub fn mut_i32(self, config: &mut GeneralConfig) -> &mut i32 {
|
||||||
match self {
|
match self {
|
||||||
Self::ClickFreezeTimeMs => &mut config.click_freeze_time_ms,
|
Self::ClickFreezeTimeMs => &mut config.click_freeze_time_ms,
|
||||||
_ => panic!("Requested i32 for non-i32 SettingType"),
|
_ => panic!("Requested i32 for non-i32 SettingType"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_enum<'a>(self, config: &'a mut GeneralConfig, value: &str) {
|
pub fn set_enum(self, config: &mut GeneralConfig, value: &str) {
|
||||||
match self {
|
match self {
|
||||||
Self::CaptureMethod => {
|
Self::CaptureMethod => {
|
||||||
config.capture_method = wlx_common::config::CaptureMethod::from_str(value).expect("Invalid enum value!")
|
config.capture_method = wlx_common::config::CaptureMethod::from_str(value).expect("Invalid enum value!")
|
||||||
@@ -247,7 +290,7 @@ impl SettingType {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_enum_title<'a>(self, config: &'a mut GeneralConfig) -> Translation {
|
fn get_enum_title(self, config: &mut GeneralConfig) -> Translation {
|
||||||
match self {
|
match self {
|
||||||
Self::CaptureMethod => Self::get_enum_title_inner(config.capture_method),
|
Self::CaptureMethod => Self::get_enum_title_inner(config.capture_method),
|
||||||
Self::KeyboardMiddleClick => Self::get_enum_title_inner(config.keyboard_middle_click_mode),
|
Self::KeyboardMiddleClick => Self::get_enum_title_inner(config.keyboard_middle_click_mode),
|
||||||
@@ -261,8 +304,8 @@ impl SettingType {
|
|||||||
{
|
{
|
||||||
value
|
value
|
||||||
.get_str("Translation")
|
.get_str("Translation")
|
||||||
.map(|x| Translation::from_translation_key(x))
|
.map(Translation::from_translation_key)
|
||||||
.or_else(|| value.get_str("Text").map(|x| Translation::from_raw_text(x)))
|
.or_else(|| value.get_str("Text").map(Translation::from_raw_text))
|
||||||
.unwrap_or_else(|| Translation::from_raw_text(value.as_ref()))
|
.unwrap_or_else(|| Translation::from_raw_text(value.as_ref()))
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +313,7 @@ impl SettingType {
|
|||||||
where
|
where
|
||||||
E: EnumProperty + AsRef<str>,
|
E: EnumProperty + AsRef<str>,
|
||||||
{
|
{
|
||||||
value.get_str("Tooltip").map(|x| Translation::from_translation_key(x))
|
value.get_str("Tooltip").map(Translation::from_translation_key)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Ok is translation, Err is raw text
|
/// Ok is translation, Err is raw text
|
||||||
@@ -517,9 +560,9 @@ macro_rules! dropdown {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let btn = $mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
|
let btn = $mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
|
||||||
btn.on_click(Box::new({
|
btn.on_click(Rc::new({
|
||||||
let tasks = $mp.tasks.clone();
|
let tasks = $mp.tasks.clone();
|
||||||
move |_common, e| {
|
move |_common, e: ButtonClickEvent| {
|
||||||
tasks.push(Task::OpenContextMenu(
|
tasks.push(Task::OpenContextMenu(
|
||||||
e.mouse_pos_absolute.unwrap_or_default(),
|
e.mouse_pos_absolute.unwrap_or_default(),
|
||||||
$options
|
$options
|
||||||
@@ -566,7 +609,7 @@ macro_rules! danger_button {
|
|||||||
.instantiate_template($mp.doc_params, "DangerButton", $mp.layout, $root, params)?;
|
.instantiate_template($mp.doc_params, "DangerButton", $mp.layout, $root, params)?;
|
||||||
|
|
||||||
let btn = $mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
|
let btn = $mp.parser_state.fetch_component_as::<ComponentButton>(&id)?;
|
||||||
btn.on_click(Box::new({
|
btn.on_click(Rc::new({
|
||||||
let tasks = $mp.tasks.clone();
|
let tasks = $mp.tasks.clone();
|
||||||
move |_common, _e| {
|
move |_common, _e| {
|
||||||
tasks.push($task);
|
tasks.push($task);
|
||||||
@@ -594,7 +637,7 @@ macro_rules! autostart_app {
|
|||||||
|
|
||||||
$ids.push(id.clone());
|
$ids.push(id.clone());
|
||||||
|
|
||||||
btn.on_click(Box::new({
|
btn.on_click(Rc::new({
|
||||||
let tasks = $mp.tasks.clone();
|
let tasks = $mp.tasks.clone();
|
||||||
move |_common, _e| {
|
move |_common, _e| {
|
||||||
tasks.push(Task::RemoveAutostartApp(id.clone()));
|
tasks.push(Task::RemoveAutostartApp(id.clone()));
|
||||||
@@ -613,121 +656,158 @@ struct MacroParams<'a> {
|
|||||||
idx: usize,
|
idx: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn doc_params(globals: &'_ WguiGlobals) -> ParseDocumentParams<'_> {
|
||||||
|
ParseDocumentParams {
|
||||||
|
globals: globals.clone(),
|
||||||
|
path: AssetPath::BuiltIn("gui/tab/settings.xml"),
|
||||||
|
extra: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> TabSettings<T> {
|
impl<T> TabSettings<T> {
|
||||||
pub fn new(frontend: &mut Frontend<T>, parent_id: WidgetID, data: &mut T) -> anyhow::Result<Self> {
|
fn set_tab(&mut self, frontend: &mut Frontend<T>, data: &mut T, name: TabNameEnum) -> anyhow::Result<()> {
|
||||||
|
let root = self.state.get_widget_id("settings_root")?;
|
||||||
|
frontend.layout.remove_children(root);
|
||||||
|
let globals = frontend.layout.state.globals.clone();
|
||||||
|
|
||||||
|
let mut mp = MacroParams {
|
||||||
|
layout: &mut frontend.layout,
|
||||||
|
parser_state: &mut self.state,
|
||||||
|
doc_params: &doc_params(&globals),
|
||||||
|
config: frontend.interface.general_config(data),
|
||||||
|
tasks: self.tasks.clone(),
|
||||||
|
idx: 9001,
|
||||||
|
};
|
||||||
|
|
||||||
|
match name {
|
||||||
|
TabNameEnum::LookAndFeel => {
|
||||||
|
let c = category!(mp, root, "APP_SETTINGS.LOOK_AND_FEEL", "dashboard/palette.svg")?;
|
||||||
|
checkbox!(mp, c, SettingType::OpaqueBackground);
|
||||||
|
checkbox!(mp, c, SettingType::HideUsername);
|
||||||
|
checkbox!(mp, c, SettingType::HideGrabHelp);
|
||||||
|
slider_f32!(mp, c, SettingType::AnimationSpeed, 0.5, 5.0, 0.1); // min, max, step
|
||||||
|
slider_f32!(mp, c, SettingType::RoundMultiplier, 0.5, 5.0, 0.1);
|
||||||
|
checkbox!(mp, c, SettingType::SetsOnWatch);
|
||||||
|
checkbox!(mp, c, SettingType::UseSkybox);
|
||||||
|
checkbox!(mp, c, SettingType::UsePassthrough);
|
||||||
|
checkbox!(mp, c, SettingType::Clock12h);
|
||||||
|
}
|
||||||
|
TabNameEnum::Features => {
|
||||||
|
let c = category!(mp, root, "APP_SETTINGS.FEATURES", "dashboard/options.svg")?;
|
||||||
|
checkbox!(mp, c, SettingType::NotificationsEnabled);
|
||||||
|
checkbox!(mp, c, SettingType::NotificationsSoundEnabled);
|
||||||
|
checkbox!(mp, c, SettingType::KeyboardSoundEnabled);
|
||||||
|
checkbox!(mp, c, SettingType::SpaceDragUnlocked);
|
||||||
|
checkbox!(mp, c, SettingType::SpaceRotateUnlocked);
|
||||||
|
slider_f32!(mp, c, SettingType::SpaceDragMultiplier, -10.0, 10.0, 0.5);
|
||||||
|
checkbox!(mp, c, SettingType::BlockGameInput);
|
||||||
|
checkbox!(mp, c, SettingType::BlockGameInputIgnoreWatch);
|
||||||
|
}
|
||||||
|
TabNameEnum::Controls => {
|
||||||
|
let c = category!(mp, root, "APP_SETTINGS.CONTROLS", "dashboard/controller.svg")?;
|
||||||
|
dropdown!(
|
||||||
|
mp,
|
||||||
|
c,
|
||||||
|
SettingType::KeyboardMiddleClick,
|
||||||
|
wlx_common::config::AltModifier::VARIANTS
|
||||||
|
);
|
||||||
|
checkbox!(mp, c, SettingType::FocusFollowsMouseMode);
|
||||||
|
checkbox!(mp, c, SettingType::LeftHandedMouse);
|
||||||
|
checkbox!(mp, c, SettingType::AllowSliding);
|
||||||
|
checkbox!(mp, c, SettingType::InvertScrollDirectionX);
|
||||||
|
checkbox!(mp, c, SettingType::InvertScrollDirectionY);
|
||||||
|
slider_f32!(mp, c, SettingType::ScrollSpeed, 0.1, 5.0, 0.1);
|
||||||
|
slider_f32!(mp, c, SettingType::LongPressDuration, 0.1, 2.0, 0.1);
|
||||||
|
slider_f32!(mp, c, SettingType::PointerLerpFactor, 0.1, 1.0, 0.1);
|
||||||
|
slider_f32!(mp, c, SettingType::XrClickSensitivity, 0.1, 1.0, 0.1);
|
||||||
|
slider_f32!(mp, c, SettingType::XrClickSensitivityRelease, 0.1, 1.0, 0.1);
|
||||||
|
slider_i32!(mp, c, SettingType::ClickFreezeTimeMs, 0, 500, 50);
|
||||||
|
}
|
||||||
|
TabNameEnum::Misc => {
|
||||||
|
let c = category!(mp, root, "APP_SETTINGS.MISC", "dashboard/blocks.svg")?;
|
||||||
|
dropdown!(
|
||||||
|
mp,
|
||||||
|
c,
|
||||||
|
SettingType::CaptureMethod,
|
||||||
|
wlx_common::config::CaptureMethod::VARIANTS
|
||||||
|
);
|
||||||
|
checkbox!(mp, c, SettingType::XwaylandByDefault);
|
||||||
|
checkbox!(mp, c, SettingType::UprightScreenFix);
|
||||||
|
checkbox!(mp, c, SettingType::DoubleCursorFix);
|
||||||
|
checkbox!(mp, c, SettingType::ScreenRenderDown);
|
||||||
|
}
|
||||||
|
TabNameEnum::AutostartApps => {
|
||||||
|
self.app_button_ids = vec![];
|
||||||
|
|
||||||
|
if !mp.config.autostart_apps.is_empty() {
|
||||||
|
let c = category!(mp, root, "APP_SETTINGS.AUTOSTART_APPS", "dashboard/apps.svg")?;
|
||||||
|
|
||||||
|
for app in &mp.config.autostart_apps {
|
||||||
|
autostart_app!(mp, c, app.name, self.app_button_ids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TabNameEnum::Troubleshooting => {
|
||||||
|
let c = category!(mp, root, "APP_SETTINGS.TROUBLESHOOTING", "dashboard/cpu.svg")?;
|
||||||
|
danger_button!(
|
||||||
|
mp,
|
||||||
|
c,
|
||||||
|
"APP_SETTINGS.CLEAR_PIPEWIRE_TOKENS",
|
||||||
|
"dashboard/display.svg",
|
||||||
|
Task::ClearPipewireTokens
|
||||||
|
);
|
||||||
|
danger_button!(
|
||||||
|
mp,
|
||||||
|
c,
|
||||||
|
"APP_SETTINGS.CLEAR_SAVED_STATE",
|
||||||
|
"dashboard/binary.svg",
|
||||||
|
Task::ClearSavedState
|
||||||
|
);
|
||||||
|
danger_button!(
|
||||||
|
mp,
|
||||||
|
c,
|
||||||
|
"APP_SETTINGS.DELETE_ALL_CONFIGS",
|
||||||
|
"dashboard/circle.svg",
|
||||||
|
Task::DeleteAllConfigs
|
||||||
|
);
|
||||||
|
danger_button!(
|
||||||
|
mp,
|
||||||
|
c,
|
||||||
|
"APP_SETTINGS.RESTART_SOFTWARE",
|
||||||
|
"dashboard/refresh.svg",
|
||||||
|
Task::RestartSoftware
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn new(frontend: &mut Frontend<T>, parent_id: WidgetID, _data: &mut T) -> anyhow::Result<Self> {
|
||||||
let doc_params = ParseDocumentParams {
|
let doc_params = ParseDocumentParams {
|
||||||
globals: frontend.layout.state.globals.clone(),
|
globals: frontend.layout.state.globals.clone(),
|
||||||
path: AssetPath::BuiltIn("gui/tab/settings.xml"),
|
path: AssetPath::BuiltIn("gui/tab/settings.xml"),
|
||||||
extra: Default::default(),
|
extra: Default::default(),
|
||||||
};
|
};
|
||||||
let mut parser_state = wgui::parser::parse_from_assets(&doc_params, &mut frontend.layout, parent_id)?;
|
|
||||||
|
|
||||||
let root = parser_state.get_widget_id("settings_root")?;
|
let parser_state = wgui::parser::parse_from_assets(&doc_params, &mut frontend.layout, parent_id)?;
|
||||||
|
let tasks = Tasks::default();
|
||||||
|
let tabs = parser_state.fetch_component_as::<ComponentTabs>("tabs")?;
|
||||||
|
tabs.on_select({
|
||||||
|
let tasks = tasks.clone();
|
||||||
|
Rc::new(move |_common, evt| {
|
||||||
|
if let Some(tab) = TabNameEnum::from_string(&evt.name) {
|
||||||
|
tasks.push(Task::SetTab(tab));
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
let mut mp = MacroParams {
|
tasks.push(Task::SetTab(TabNameEnum::LookAndFeel));
|
||||||
layout: &mut frontend.layout,
|
|
||||||
parser_state: &mut parser_state,
|
|
||||||
doc_params: &doc_params,
|
|
||||||
config: frontend.interface.general_config(data),
|
|
||||||
tasks: Tasks::default(),
|
|
||||||
idx: 9001,
|
|
||||||
};
|
|
||||||
|
|
||||||
let c = category!(mp, root, "APP_SETTINGS.LOOK_AND_FEEL", "dashboard/palette.svg")?;
|
|
||||||
checkbox!(mp, c, SettingType::OpaqueBackground);
|
|
||||||
checkbox!(mp, c, SettingType::HideUsername);
|
|
||||||
checkbox!(mp, c, SettingType::HideGrabHelp);
|
|
||||||
slider_f32!(mp, c, SettingType::AnimationSpeed, 0.5, 5.0, 0.1); // min, max, step
|
|
||||||
slider_f32!(mp, c, SettingType::RoundMultiplier, 0.5, 5.0, 0.1);
|
|
||||||
checkbox!(mp, c, SettingType::SetsOnWatch);
|
|
||||||
checkbox!(mp, c, SettingType::UseSkybox);
|
|
||||||
checkbox!(mp, c, SettingType::UsePassthrough);
|
|
||||||
checkbox!(mp, c, SettingType::Clock12h);
|
|
||||||
|
|
||||||
let c = category!(mp, root, "APP_SETTINGS.FEATURES", "dashboard/options.svg")?;
|
|
||||||
checkbox!(mp, c, SettingType::NotificationsEnabled);
|
|
||||||
checkbox!(mp, c, SettingType::NotificationsSoundEnabled);
|
|
||||||
checkbox!(mp, c, SettingType::KeyboardSoundEnabled);
|
|
||||||
checkbox!(mp, c, SettingType::SpaceDragUnlocked);
|
|
||||||
checkbox!(mp, c, SettingType::SpaceRotateUnlocked);
|
|
||||||
slider_f32!(mp, c, SettingType::SpaceDragMultiplier, -10.0, 10.0, 0.5);
|
|
||||||
checkbox!(mp, c, SettingType::BlockGameInput);
|
|
||||||
checkbox!(mp, c, SettingType::BlockGameInputIgnoreWatch);
|
|
||||||
|
|
||||||
let c = category!(mp, root, "APP_SETTINGS.CONTROLS", "dashboard/controller.svg")?;
|
|
||||||
dropdown!(
|
|
||||||
mp,
|
|
||||||
c,
|
|
||||||
SettingType::KeyboardMiddleClick,
|
|
||||||
wlx_common::config::AltModifier::VARIANTS
|
|
||||||
);
|
|
||||||
checkbox!(mp, c, SettingType::FocusFollowsMouseMode);
|
|
||||||
checkbox!(mp, c, SettingType::LeftHandedMouse);
|
|
||||||
checkbox!(mp, c, SettingType::AllowSliding);
|
|
||||||
checkbox!(mp, c, SettingType::InvertScrollDirectionX);
|
|
||||||
checkbox!(mp, c, SettingType::InvertScrollDirectionY);
|
|
||||||
slider_f32!(mp, c, SettingType::ScrollSpeed, 0.1, 5.0, 0.1);
|
|
||||||
slider_f32!(mp, c, SettingType::LongPressDuration, 0.1, 2.0, 0.1);
|
|
||||||
slider_f32!(mp, c, SettingType::PointerLerpFactor, 0.1, 1.0, 0.1);
|
|
||||||
slider_f32!(mp, c, SettingType::XrClickSensitivity, 0.1, 1.0, 0.1);
|
|
||||||
slider_f32!(mp, c, SettingType::XrClickSensitivityRelease, 0.1, 1.0, 0.1);
|
|
||||||
slider_i32!(mp, c, SettingType::ClickFreezeTimeMs, 0, 500, 50);
|
|
||||||
|
|
||||||
let c = category!(mp, root, "APP_SETTINGS.MISC", "dashboard/blocks.svg")?;
|
|
||||||
dropdown!(
|
|
||||||
mp,
|
|
||||||
c,
|
|
||||||
SettingType::CaptureMethod,
|
|
||||||
wlx_common::config::CaptureMethod::VARIANTS
|
|
||||||
);
|
|
||||||
checkbox!(mp, c, SettingType::XwaylandByDefault);
|
|
||||||
checkbox!(mp, c, SettingType::UprightScreenFix);
|
|
||||||
checkbox!(mp, c, SettingType::DoubleCursorFix);
|
|
||||||
checkbox!(mp, c, SettingType::ScreenRenderDown);
|
|
||||||
|
|
||||||
let mut app_button_ids = vec![];
|
|
||||||
|
|
||||||
if !mp.config.autostart_apps.is_empty() {
|
|
||||||
let c = category!(mp, root, "APP_SETTINGS.AUTOSTART_APPS", "dashboard/apps.svg")?;
|
|
||||||
|
|
||||||
for app in &mp.config.autostart_apps {
|
|
||||||
autostart_app!(mp, c, app.name, app_button_ids);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let c = category!(mp, root, "APP_SETTINGS.TROUBLESHOOTING", "dashboard/cpu.svg")?;
|
|
||||||
danger_button!(
|
|
||||||
mp,
|
|
||||||
c,
|
|
||||||
"APP_SETTINGS.CLEAR_PIPEWIRE_TOKENS",
|
|
||||||
"dashboard/display.svg",
|
|
||||||
Task::ClearPipewireTokens
|
|
||||||
);
|
|
||||||
danger_button!(
|
|
||||||
mp,
|
|
||||||
c,
|
|
||||||
"APP_SETTINGS.CLEAR_SAVED_STATE",
|
|
||||||
"dashboard/binary.svg",
|
|
||||||
Task::ClearSavedState
|
|
||||||
);
|
|
||||||
danger_button!(
|
|
||||||
mp,
|
|
||||||
c,
|
|
||||||
"APP_SETTINGS.DELETE_ALL_CONFIGS",
|
|
||||||
"dashboard/circle.svg",
|
|
||||||
Task::DeleteAllConfigs
|
|
||||||
);
|
|
||||||
danger_button!(
|
|
||||||
mp,
|
|
||||||
c,
|
|
||||||
"APP_SETTINGS.RESTART_SOFTWARE",
|
|
||||||
"dashboard/refresh.svg",
|
|
||||||
Task::RestartSoftware
|
|
||||||
);
|
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
app_button_ids,
|
app_button_ids: Vec::new(),
|
||||||
tasks: mp.tasks,
|
tasks,
|
||||||
state: parser_state,
|
state: parser_state,
|
||||||
marker: PhantomData,
|
marker: PhantomData,
|
||||||
context_menu: ContextMenu::default(),
|
context_menu: ContextMenu::default(),
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ impl PopupManager {
|
|||||||
|
|
||||||
but_back.on_click({
|
but_back.on_click({
|
||||||
let popup_handle = Rc::downgrade(&popup_handle.state);
|
let popup_handle = Rc::downgrade(&popup_handle.state);
|
||||||
Box::new(move |_common, _evt| {
|
Rc::new(move |_common, _evt| {
|
||||||
if let Some(popup_handle) = popup_handle.upgrade() {
|
if let Some(popup_handle) = popup_handle.upgrade() {
|
||||||
popup_handle.borrow_mut().mounted_popup = None; // will call Drop
|
popup_handle.borrow_mut().mounted_popup = None; // will call Drop
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ fn mount_multi_selector(params: MultiSelectorParams) -> anyhow::Result<()> {
|
|||||||
button.on_click({
|
button.on_click({
|
||||||
let on_click = params.on_click.clone();
|
let on_click = params.on_click.clone();
|
||||||
let key = cell.key.clone();
|
let key = cell.key.clone();
|
||||||
Box::new(move |_, _| {
|
Rc::new(move |_, _| {
|
||||||
(*on_click)(key.as_str());
|
(*on_click)(key.as_str());
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@@ -610,7 +610,7 @@ impl View {
|
|||||||
fn handle_func_button_click(&self, task: ViewTask) -> ButtonClickCallback {
|
fn handle_func_button_click(&self, task: ViewTask) -> ButtonClickCallback {
|
||||||
let tasks = self.tasks.clone();
|
let tasks = self.tasks.clone();
|
||||||
let on_update = self.on_update.clone();
|
let on_update = self.on_update.clone();
|
||||||
Box::new(move |_common, _evt| {
|
Rc::new(move |_common, _evt| {
|
||||||
tasks.push(task.clone());
|
tasks.push(task.clone());
|
||||||
(*on_update)();
|
(*on_update)();
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -762,7 +762,7 @@ impl View {
|
|||||||
let tasks = self.tasks.clone();
|
let tasks = self.tasks.clone();
|
||||||
let card = params.card.clone();
|
let card = params.card.clone();
|
||||||
let on_update = self.on_update.clone();
|
let on_update = self.on_update.clone();
|
||||||
Box::new(move |_common, _evt| {
|
Rc::new(move |_common, _evt| {
|
||||||
tasks.push(ViewTask::SetMode(CurrentMode::CardProfileSelector(card.clone())));
|
tasks.push(ViewTask::SetMode(CurrentMode::CardProfileSelector(card.clone())));
|
||||||
(*on_update)();
|
(*on_update)();
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -836,7 +836,7 @@ impl View {
|
|||||||
|
|
||||||
btn_mute.on_click({
|
btn_mute.on_click({
|
||||||
let control = params.control.clone();
|
let control = params.control.clone();
|
||||||
Box::new(move |_common, _event| {
|
Rc::new(move |_common, _event| {
|
||||||
control.on_mute_toggle()?;
|
control.on_mute_toggle()?;
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@@ -957,7 +957,7 @@ impl View {
|
|||||||
btn_back.on_click({
|
btn_back.on_click({
|
||||||
let tasks = self.tasks.clone();
|
let tasks = self.tasks.clone();
|
||||||
let on_update = self.on_update.clone();
|
let on_update = self.on_update.clone();
|
||||||
Box::new(move |_, _| {
|
Rc::new(move |_, _| {
|
||||||
tasks.push(ViewTask::SetMode(CurrentMode::Cards));
|
tasks.push(ViewTask::SetMode(CurrentMode::Cards));
|
||||||
(*on_update)();
|
(*on_update)();
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@@ -150,7 +150,7 @@ fn fill_game_list(
|
|||||||
view_cover.button.on_click({
|
view_cover.button.on_click({
|
||||||
let tasks = tasks.clone();
|
let tasks = tasks.clone();
|
||||||
let manifest = manifest.clone();
|
let manifest = manifest.clone();
|
||||||
Box::new(move |_, _| {
|
Rc::new(move |_, _| {
|
||||||
tasks.push(Task::AppManifestClicked(manifest.clone()));
|
tasks.push(Task::AppManifestClicked(manifest.clone()));
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -186,7 +186,7 @@ fn fill_process_list(
|
|||||||
entry_res.btn_terminate.on_click({
|
entry_res.btn_terminate.on_click({
|
||||||
let tasks = tasks.clone();
|
let tasks = tasks.clone();
|
||||||
let entry = process_entry.clone();
|
let entry = process_entry.clone();
|
||||||
Box::new(move |_, _| {
|
Rc::new(move |_, _| {
|
||||||
tasks.push(Task::TerminateProcess(entry.clone()));
|
tasks.push(Task::TerminateProcess(entry.clone()));
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -190,7 +190,7 @@ fn fill_window_list<T>(
|
|||||||
|
|
||||||
button.on_click({
|
button.on_click({
|
||||||
let tasks = tasks.clone();
|
let tasks = tasks.clone();
|
||||||
Box::new(move |_, _| {
|
Rc::new(move |_, _| {
|
||||||
tasks.push(Task::WindowClicked(entry.clone()));
|
tasks.push(Task::WindowClicked(entry.clone()));
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@@ -260,14 +260,14 @@ impl View {
|
|||||||
self.frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams {
|
self.frontend_tasks.push(FrontendTask::MountPopup(MountPopupParams {
|
||||||
title: Translation::from_translation_key("WINDOW_OPTIONS"),
|
title: Translation::from_translation_key("WINDOW_OPTIONS"),
|
||||||
on_content: {
|
on_content: {
|
||||||
let frontend_tasks = self.frontend_tasks.clone();
|
let _frontend_tasks = self.frontend_tasks.clone();
|
||||||
let globals = self.globals.clone();
|
let _globals = self.globals.clone();
|
||||||
let state = self.state.clone();
|
let _state = self.state.clone();
|
||||||
let tasks = self.tasks.clone();
|
let _tasks = self.tasks.clone();
|
||||||
|
|
||||||
//TODO
|
//TODO
|
||||||
|
|
||||||
Rc::new(move |data| {
|
Rc::new(move |_data| {
|
||||||
// state.borrow_mut().view_window_options = Some((
|
// state.borrow_mut().view_window_options = Some((
|
||||||
// data.handle,
|
// data.handle,
|
||||||
// window_options::View::new(window_options::Params {
|
// window_options::View::new(window_options::Params {
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ fn button_click_callback(
|
|||||||
label: Widget,
|
label: Widget,
|
||||||
text: &'static str,
|
text: &'static str,
|
||||||
) -> ButtonClickCallback {
|
) -> ButtonClickCallback {
|
||||||
Box::new(move |common, _e| {
|
Rc::new(move |common, _e| {
|
||||||
label
|
label
|
||||||
.get_as::<WidgetLabel>()
|
.get_as::<WidgetLabel>()
|
||||||
.unwrap()
|
.unwrap()
|
||||||
@@ -148,7 +148,7 @@ impl TestbedGeneric {
|
|||||||
parser_state.fetch_component_as::<ComponentButton>("button_context_menu")?;
|
parser_state.fetch_component_as::<ComponentButton>("button_context_menu")?;
|
||||||
let button_click_me = parser_state.fetch_component_as::<ComponentButton>("button_click_me")?;
|
let button_click_me = parser_state.fetch_component_as::<ComponentButton>("button_click_me")?;
|
||||||
let button = button_click_me.clone();
|
let button = button_click_me.clone();
|
||||||
button_click_me.on_click(Box::new(move |common, _e| {
|
button_click_me.on_click(Rc::new(move |common, _e| {
|
||||||
button.set_text(common, Translation::from_raw_text("congrats!"));
|
button.set_text(common, Translation::from_raw_text("congrats!"));
|
||||||
Ok(())
|
Ok(())
|
||||||
}));
|
}));
|
||||||
@@ -188,7 +188,7 @@ impl TestbedGeneric {
|
|||||||
|
|
||||||
button_popup.on_click({
|
button_popup.on_click({
|
||||||
let tasks = testbed.tasks.clone();
|
let tasks = testbed.tasks.clone();
|
||||||
Box::new(move |_, _| {
|
Rc::new(move |_, _| {
|
||||||
tasks.push(TestbedTask::ShowPopup);
|
tasks.push(TestbedTask::ShowPopup);
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
@@ -196,7 +196,7 @@ impl TestbedGeneric {
|
|||||||
|
|
||||||
button_context_menu.on_click({
|
button_context_menu.on_click({
|
||||||
let tasks = testbed.tasks.clone();
|
let tasks = testbed.tasks.clone();
|
||||||
Box::new(move |_common, m| {
|
Rc::new(move |_common, m| {
|
||||||
tasks.push(TestbedTask::ShowContextMenu(m.boundary.bottom_left()));
|
tasks.push(TestbedTask::ShowContextMenu(m.boundary.bottom_left()));
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -66,11 +66,12 @@ impl Default for Params<'_> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
pub struct ButtonClickEvent {
|
pub struct ButtonClickEvent {
|
||||||
pub mouse_pos_absolute: Option<Vec2>,
|
pub mouse_pos_absolute: Option<Vec2>,
|
||||||
pub boundary: Boundary,
|
pub boundary: Boundary,
|
||||||
}
|
}
|
||||||
pub type ButtonClickCallback = Box<dyn Fn(&mut CallbackDataCommon, ButtonClickEvent) -> anyhow::Result<()>>;
|
pub type ButtonClickCallback = Rc<dyn Fn(&mut CallbackDataCommon, ButtonClickEvent) -> anyhow::Result<()>>;
|
||||||
|
|
||||||
pub struct Colors {
|
pub struct Colors {
|
||||||
pub color: drawing::Color,
|
pub color: drawing::Color,
|
||||||
@@ -111,7 +112,6 @@ impl ComponentTrait for ComponentButton {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn refresh(&self, data: &mut RefreshData) {
|
fn refresh(&self, data: &mut RefreshData) {
|
||||||
// nothing to do
|
|
||||||
let mut state = self.state.borrow_mut();
|
let mut state = self.state.borrow_mut();
|
||||||
|
|
||||||
if state.active_tooltip.is_some() {
|
if state.active_tooltip.is_some() {
|
||||||
@@ -362,7 +362,6 @@ fn register_event_mouse_release(
|
|||||||
Box::new(move |common, event_data, (), ()| {
|
Box::new(move |common, event_data, (), ()| {
|
||||||
let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
let rect = event_data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||||
let mut state = state.borrow_mut();
|
let mut state = state.borrow_mut();
|
||||||
|
|
||||||
if data.sticky {
|
if data.sticky {
|
||||||
state.sticky_down = !state.sticky_down;
|
state.sticky_down = !state.sticky_down;
|
||||||
}
|
}
|
||||||
@@ -384,17 +383,18 @@ fn register_event_mouse_release(
|
|||||||
state.sticky_down,
|
state.sticky_down,
|
||||||
);
|
);
|
||||||
|
|
||||||
if let Some(on_click) = &state.on_click {
|
if let Some(on_click) = state.on_click.clone() {
|
||||||
on_click(
|
let evt = ButtonClickEvent {
|
||||||
common,
|
mouse_pos_absolute: event_data.metadata.get_mouse_pos_absolute(),
|
||||||
ButtonClickEvent {
|
boundary: event_data.widget_data.cached_absolute_boundary,
|
||||||
mouse_pos_absolute: event_data.metadata.get_mouse_pos_absolute(),
|
};
|
||||||
boundary: event_data.widget_data.cached_absolute_boundary,
|
|
||||||
},
|
common.alterables.dispatch(Box::new(move |common| {
|
||||||
)?;
|
(*on_click)(common, evt)?;
|
||||||
|
Ok(())
|
||||||
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(EventResult::Consumed)
|
Ok(EventResult::Consumed)
|
||||||
} else {
|
} else {
|
||||||
Ok(EventResult::Pass)
|
Ok(EventResult::Pass)
|
||||||
@@ -403,14 +403,15 @@ fn register_event_mouse_release(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentButton>)> {
|
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentButton>)> {
|
||||||
let globals = ess.layout.state.globals.clone();
|
let globals = ess.layout.state.globals.clone();
|
||||||
let mut style = params.style;
|
let mut style = params.style;
|
||||||
|
|
||||||
// force-override style
|
// force-override style
|
||||||
style.align_items = Some(AlignItems::Center);
|
style.align_items = Some(AlignItems::Center);
|
||||||
style.justify_content = Some(JustifyContent::Center);
|
if style.justify_content.is_none() {
|
||||||
|
style.justify_content = Some(JustifyContent::Center);
|
||||||
|
}
|
||||||
style.overflow.x = taffy::Overflow::Hidden;
|
style.overflow.x = taffy::Overflow::Hidden;
|
||||||
style.overflow.y = taffy::Overflow::Hidden;
|
style.overflow.y = taffy::Overflow::Hidden;
|
||||||
|
|
||||||
|
|||||||
@@ -284,7 +284,6 @@ fn register_event_mouse_release(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentCheckbox>)> {
|
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentCheckbox>)> {
|
||||||
let mut style = params.style;
|
let mut style = params.style;
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ pub mod button;
|
|||||||
pub mod checkbox;
|
pub mod checkbox;
|
||||||
pub mod radio_group;
|
pub mod radio_group;
|
||||||
pub mod slider;
|
pub mod slider;
|
||||||
|
pub mod tabs;
|
||||||
pub mod tooltip;
|
pub mod tooltip;
|
||||||
|
|
||||||
pub struct RefreshData<'a> {
|
pub struct RefreshData<'a> {
|
||||||
|
|||||||
@@ -398,7 +398,6 @@ fn register_event_mouse_release(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentSlider>)> {
|
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentSlider>)> {
|
||||||
let mut style = params.style;
|
let mut style = params.style;
|
||||||
style.position = taffy::Position::Relative;
|
style.position = taffy::Position::Relative;
|
||||||
|
|||||||
176
wgui/src/components/tabs.rs
Normal file
176
wgui/src/components/tabs.rs
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
use crate::{
|
||||||
|
assets::AssetPath,
|
||||||
|
components::{
|
||||||
|
Component, ComponentBase, ComponentTrait, RefreshData,
|
||||||
|
button::{self, ComponentButton},
|
||||||
|
},
|
||||||
|
event::CallbackDataCommon,
|
||||||
|
i18n::Translation,
|
||||||
|
layout::WidgetPair,
|
||||||
|
widget::{ConstructEssentials, div::WidgetDiv},
|
||||||
|
};
|
||||||
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
|
rc::{Rc, Weak},
|
||||||
|
sync::Arc,
|
||||||
|
};
|
||||||
|
use taffy::{
|
||||||
|
AlignItems,
|
||||||
|
prelude::{auto, length, percent},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Entry<'a> {
|
||||||
|
pub sprite_src: Option<AssetPath<'a>>,
|
||||||
|
pub text: Translation,
|
||||||
|
pub name: &'a str,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Params<'a> {
|
||||||
|
pub style: taffy::Style,
|
||||||
|
pub entries: Vec<Entry<'a>>,
|
||||||
|
pub selected_entry_name: &'a str, // default: ""
|
||||||
|
pub on_select: Option<TabSelectCallback>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MountedEntry {
|
||||||
|
name: Rc<str>,
|
||||||
|
button: Rc<ComponentButton>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TabSelectEvent {
|
||||||
|
pub name: Rc<str>,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type TabSelectCallback = Rc<dyn Fn(&mut CallbackDataCommon, TabSelectEvent) -> anyhow::Result<()>>;
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
mounted_entries: Vec<MountedEntry>,
|
||||||
|
selected_entry_name: Rc<str>,
|
||||||
|
on_select: Option<TabSelectCallback>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Data {}
|
||||||
|
|
||||||
|
pub struct ComponentTabs {
|
||||||
|
base: ComponentBase,
|
||||||
|
data: Rc<Data>,
|
||||||
|
state: Rc<RefCell<State>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentTrait for ComponentTabs {
|
||||||
|
fn base(&self) -> &ComponentBase {
|
||||||
|
&self.base
|
||||||
|
}
|
||||||
|
|
||||||
|
fn base_mut(&mut self) -> &mut ComponentBase {
|
||||||
|
&mut self.base
|
||||||
|
}
|
||||||
|
|
||||||
|
fn refresh(&self, _data: &mut RefreshData) {
|
||||||
|
// nothing to do
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl State {
|
||||||
|
fn select_entry(&mut self, common: &mut CallbackDataCommon, name: &Rc<str>) {
|
||||||
|
let (color_accent, color_button) = {
|
||||||
|
let def = common.state.globals.defaults();
|
||||||
|
(def.accent_color, def.button_color)
|
||||||
|
};
|
||||||
|
|
||||||
|
for entry in &self.mounted_entries {
|
||||||
|
if *entry.name == **name {
|
||||||
|
entry.button.set_color(common, color_accent);
|
||||||
|
} else {
|
||||||
|
entry.button.set_color(common, color_button);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.selected_entry_name = name.clone();
|
||||||
|
|
||||||
|
if let Some(on_select) = self.on_select.clone() {
|
||||||
|
let evt = TabSelectEvent { name: name.clone() };
|
||||||
|
common.alterables.dispatch(Box::new(move |common| {
|
||||||
|
(*on_select)(common, evt)?;
|
||||||
|
Ok(())
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ComponentTabs {
|
||||||
|
pub fn on_select(&self, callback: TabSelectCallback) {
|
||||||
|
self.state.borrow_mut().on_select = Some(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentTabs>)> {
|
||||||
|
let mut style = params.style;
|
||||||
|
|
||||||
|
// force-override style
|
||||||
|
style.overflow.y = taffy::Overflow::Scroll;
|
||||||
|
style.flex_direction = taffy::FlexDirection::Column;
|
||||||
|
style.flex_wrap = taffy::FlexWrap::NoWrap;
|
||||||
|
style.align_items = Some(AlignItems::Center);
|
||||||
|
style.gap = length(4.0);
|
||||||
|
|
||||||
|
let (root, _) = ess.layout.add_child(ess.parent, WidgetDiv::create(), style)?;
|
||||||
|
|
||||||
|
let mut mounted_entries = Vec::<MountedEntry>::new();
|
||||||
|
|
||||||
|
// Mount entries
|
||||||
|
for entry in params.entries {
|
||||||
|
let (_, button) = button::construct(
|
||||||
|
&mut ConstructEssentials {
|
||||||
|
layout: ess.layout,
|
||||||
|
parent: root.id,
|
||||||
|
},
|
||||||
|
button::Params {
|
||||||
|
text: Some(entry.text),
|
||||||
|
sprite_src: entry.sprite_src,
|
||||||
|
style: taffy::Style {
|
||||||
|
min_size: taffy::Size {
|
||||||
|
width: percent(1.0),
|
||||||
|
height: length(32.0),
|
||||||
|
},
|
||||||
|
justify_content: Some(taffy::JustifyContent::Start),
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
..Default::default()
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
mounted_entries.push(MountedEntry {
|
||||||
|
name: Rc::from(entry.name),
|
||||||
|
button,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let data = Rc::new(Data {});
|
||||||
|
let state = Rc::new(RefCell::new(State {
|
||||||
|
selected_entry_name: Rc::from(params.selected_entry_name),
|
||||||
|
mounted_entries,
|
||||||
|
on_select: params.on_select,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// handle button clicks
|
||||||
|
for entry in &state.borrow().mounted_entries {
|
||||||
|
entry.button.on_click({
|
||||||
|
let entry_name = entry.name.clone();
|
||||||
|
let state = state.clone();
|
||||||
|
Rc::new(move |common, _| {
|
||||||
|
state.borrow_mut().select_entry(common, &entry_name);
|
||||||
|
Ok(())
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let base = ComponentBase {
|
||||||
|
id: root.id,
|
||||||
|
lhandles: Default::default(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let tabs = Rc::new(ComponentTabs { base, data, state });
|
||||||
|
|
||||||
|
ess.layout.defer_component_refresh(Component(tabs.clone()));
|
||||||
|
Ok((root, tabs))
|
||||||
|
}
|
||||||
@@ -89,7 +89,6 @@ impl Drop for ComponentTooltip {
|
|||||||
pub const TOOLTIP_COLOR: Color = Color::new(0.02, 0.02, 0.02, 0.95);
|
pub const TOOLTIP_COLOR: Color = Color::new(0.02, 0.02, 0.02, 0.95);
|
||||||
pub const TOOLTIP_BORDER_COLOR: Color = Color::new(0.4, 0.4, 0.4, 1.0);
|
pub const TOOLTIP_BORDER_COLOR: Color = Color::new(0.4, 0.4, 0.4, 1.0);
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentTooltip>)> {
|
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentTooltip>)> {
|
||||||
let absolute_boundary = {
|
let absolute_boundary = {
|
||||||
let widget_to_watch = ess
|
let widget_to_watch = ess
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ use slotmap::{DenseSlotMap, new_key_type};
|
|||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
animation::{self, Animation},
|
animation::{self, Animation},
|
||||||
|
components::Component,
|
||||||
i18n::I18n,
|
i18n::I18n,
|
||||||
layout::{LayoutState, LayoutTask, WidgetID},
|
layout::{LayoutState, LayoutTask, WidgetID},
|
||||||
sound::WguiSoundType,
|
sound::WguiSoundType,
|
||||||
@@ -145,6 +146,10 @@ impl EventAlterables {
|
|||||||
pub fn play_sound(&mut self, sound_type: WguiSoundType) {
|
pub fn play_sound(&mut self, sound_type: WguiSoundType) {
|
||||||
self.tasks.push(LayoutTask::PlaySound(sound_type));
|
self.tasks.push(LayoutTask::PlaySound(sound_type));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dispatch(&mut self, func: Box<dyn FnOnce(&mut CallbackDataCommon) -> anyhow::Result<()>>) {
|
||||||
|
self.tasks.push(LayoutTask::Dispatch(func))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct CallbackDataCommon<'a> {
|
pub struct CallbackDataCommon<'a> {
|
||||||
|
|||||||
@@ -136,6 +136,7 @@ pub enum LayoutTask {
|
|||||||
RemoveWidget(WidgetID),
|
RemoveWidget(WidgetID),
|
||||||
ModifyLayoutState(ModifyLayoutStateFunc),
|
ModifyLayoutState(ModifyLayoutStateFunc),
|
||||||
PlaySound(WguiSoundType),
|
PlaySound(WguiSoundType),
|
||||||
|
Dispatch(Box<dyn FnOnce(&mut CallbackDataCommon) -> anyhow::Result<()>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
pub type LayoutTasks = Tasks<LayoutTask>;
|
pub type LayoutTasks = Tasks<LayoutTask>;
|
||||||
@@ -696,6 +697,11 @@ impl Layout {
|
|||||||
self.sounds_to_play_once.push(sound);
|
self.sounds_to_play_once.push(sound);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
LayoutTask::Dispatch(func) => {
|
||||||
|
let mut c = self.start_common();
|
||||||
|
func(&mut c.common())?;
|
||||||
|
c.finish()?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -17,7 +17,8 @@
|
|||||||
clippy::float_cmp,
|
clippy::float_cmp,
|
||||||
clippy::needless_pass_by_ref_mut,
|
clippy::needless_pass_by_ref_mut,
|
||||||
clippy::use_self,
|
clippy::use_self,
|
||||||
clippy::match_same_arms
|
clippy::match_same_arms,
|
||||||
|
clippy::too_many_lines
|
||||||
)]
|
)]
|
||||||
|
|
||||||
pub mod animation;
|
pub mod animation;
|
||||||
|
|||||||
@@ -5,13 +5,12 @@ use crate::{
|
|||||||
i18n::Translation,
|
i18n::Translation,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{
|
parser::{
|
||||||
AttribPair, ParserContext, ParserFile, parse_children, parse_f32, process_component,
|
AttribPair, ParserContext, ParserFile, get_asset_path_from_kv, parse_children, parse_f32, process_component,
|
||||||
style::{parse_color_opt, parse_round, parse_style, parse_text_style},
|
style::{parse_color_opt, parse_round, parse_style, parse_text_style},
|
||||||
},
|
},
|
||||||
widget::util::WLength,
|
widget::util::WLength,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
pub fn parse_component_button<'a>(
|
pub fn parse_component_button<'a>(
|
||||||
file: &'a ParserFile,
|
file: &'a ParserFile,
|
||||||
ctx: &mut ParserContext,
|
ctx: &mut ParserContext,
|
||||||
@@ -76,13 +75,7 @@ pub fn parse_component_button<'a>(
|
|||||||
parse_color_opt(ctx, tag_name, key, value, &mut hover_border_color);
|
parse_color_opt(ctx, tag_name, key, value, &mut hover_border_color);
|
||||||
}
|
}
|
||||||
"sprite_src" | "sprite_src_ext" | "sprite_src_builtin" | "sprite_src_internal" => {
|
"sprite_src" | "sprite_src_ext" | "sprite_src_builtin" | "sprite_src_internal" => {
|
||||||
let asset_path = match key {
|
let asset_path = get_asset_path_from_kv("sprite_", key, value);
|
||||||
"sprite_src" => AssetPath::FileOrBuiltIn(value),
|
|
||||||
"sprite_src_ext" => AssetPath::File(value),
|
|
||||||
"sprite_src_builtin" => AssetPath::BuiltIn(value),
|
|
||||||
"sprite_src_internal" => AssetPath::WguiInternal(value),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if !value.is_empty() {
|
if !value.is_empty() {
|
||||||
sprite_src = Some(asset_path);
|
sprite_src = Some(asset_path);
|
||||||
|
|||||||
71
wgui/src/parser/component_tabs.rs
Normal file
71
wgui/src/parser/component_tabs.rs
Normal file
@@ -0,0 +1,71 @@
|
|||||||
|
use std::rc::Rc;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
assets::AssetPath,
|
||||||
|
components::{Component, tabs},
|
||||||
|
i18n::Translation,
|
||||||
|
layout::WidgetID,
|
||||||
|
parser::{AttribPair, ParserContext, ParserFile, get_asset_path_from_kv, process_component, style::parse_style},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn parse_component_tabs<'a>(
|
||||||
|
file: &'a ParserFile,
|
||||||
|
ctx: &mut ParserContext,
|
||||||
|
node: roxmltree::Node<'a, 'a>,
|
||||||
|
parent_id: WidgetID,
|
||||||
|
attribs: &[AttribPair],
|
||||||
|
tag_name: &str,
|
||||||
|
) -> anyhow::Result<WidgetID> {
|
||||||
|
let style = parse_style(ctx, attribs, tag_name);
|
||||||
|
|
||||||
|
let mut entries = Vec::<tabs::Entry>::new();
|
||||||
|
|
||||||
|
for child in node.children() {
|
||||||
|
match child.tag_name().name() {
|
||||||
|
"" => { /* ignore */ }
|
||||||
|
"Tab" => {
|
||||||
|
let mut name: Option<&str> = None;
|
||||||
|
let mut text: Option<Translation> = None;
|
||||||
|
let mut sprite_src: Option<AssetPath> = None;
|
||||||
|
|
||||||
|
for attrib in child.attributes() {
|
||||||
|
let (key, value) = (attrib.name(), attrib.value());
|
||||||
|
match key {
|
||||||
|
"name" => name = Some(value),
|
||||||
|
"text" => text = Some(Translation::from_raw_text(value)),
|
||||||
|
"translation" => text = Some(Translation::from_translation_key(value)),
|
||||||
|
"sprite_src" | "sprite_src_ext" | "sprite_src_builtin" | "sprite_src_internal" => {
|
||||||
|
sprite_src = Some(get_asset_path_from_kv("sprite_", key, value));
|
||||||
|
}
|
||||||
|
other_key => {
|
||||||
|
ctx.print_invalid_attrib("Tab", other_key, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(name) = name
|
||||||
|
&& let Some(text) = text
|
||||||
|
{
|
||||||
|
entries.push(tabs::Entry { sprite_src, text, name });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
other_tag_name => {
|
||||||
|
ctx.print_invalid_tag(tag_name, other_tag_name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let (widget, component) = tabs::construct(
|
||||||
|
&mut ctx.get_construct_essentials(parent_id),
|
||||||
|
tabs::Params {
|
||||||
|
style,
|
||||||
|
selected_entry_name: "first",
|
||||||
|
entries,
|
||||||
|
on_select: None,
|
||||||
|
},
|
||||||
|
)?;
|
||||||
|
|
||||||
|
process_component(ctx, Component(component), widget.id, attribs);
|
||||||
|
|
||||||
|
Ok(widget.id)
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ mod component_button;
|
|||||||
mod component_checkbox;
|
mod component_checkbox;
|
||||||
mod component_radio_group;
|
mod component_radio_group;
|
||||||
mod component_slider;
|
mod component_slider;
|
||||||
|
mod component_tabs;
|
||||||
mod style;
|
mod style;
|
||||||
mod widget_div;
|
mod widget_div;
|
||||||
mod widget_image;
|
mod widget_image;
|
||||||
@@ -22,6 +23,7 @@ use crate::{
|
|||||||
component_checkbox::{CheckboxKind, parse_component_checkbox},
|
component_checkbox::{CheckboxKind, parse_component_checkbox},
|
||||||
component_radio_group::parse_component_radio_group,
|
component_radio_group::parse_component_radio_group,
|
||||||
component_slider::parse_component_slider,
|
component_slider::parse_component_slider,
|
||||||
|
component_tabs::parse_component_tabs,
|
||||||
widget_div::parse_widget_div,
|
widget_div::parse_widget_div,
|
||||||
widget_image::parse_widget_image,
|
widget_image::parse_widget_image,
|
||||||
widget_label::parse_widget_label,
|
widget_label::parse_widget_label,
|
||||||
@@ -480,6 +482,7 @@ impl ParserContext<'_> {
|
|||||||
insert_color_vars!(self, "faded", def.faded_color, def.translucent_alpha);
|
insert_color_vars!(self, "faded", def.faded_color, def.translucent_alpha);
|
||||||
insert_color_vars!(self, "bg", def.bg_color, def.translucent_alpha);
|
insert_color_vars!(self, "bg", def.bg_color, def.translucent_alpha);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn print_invalid_attrib(&self, tag_name: &str, key: &str, value: &str) {
|
fn print_invalid_attrib(&self, tag_name: &str, key: &str, value: &str) {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"{}: <{tag_name}> value for \"{key}\" is invalid: \"{value}\"",
|
"{}: <{tag_name}> value for \"{key}\" is invalid: \"{value}\"",
|
||||||
@@ -487,6 +490,13 @@ impl ParserContext<'_> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn print_invalid_tag(&self, tag_name: &str, invalid_tag_name: &str) {
|
||||||
|
log::warn!(
|
||||||
|
"{}: <{tag_name}> has an invalid tag named <{invalid_tag_name}>",
|
||||||
|
self.doc_params.path.get_str()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
fn print_missing_attrib(&self, tag_name: &str, attr: &str) {
|
fn print_missing_attrib(&self, tag_name: &str, attr: &str) {
|
||||||
log::warn!(
|
log::warn!(
|
||||||
"{}: <{tag_name}> is missing \"{attr}\".",
|
"{}: <{tag_name}> is missing \"{attr}\".",
|
||||||
@@ -1042,6 +1052,11 @@ fn parse_child<'a>(
|
|||||||
file, ctx, child_node, parent_id, &attribs, tag_name,
|
file, ctx, child_node, parent_id, &attribs, tag_name,
|
||||||
)?);
|
)?);
|
||||||
}
|
}
|
||||||
|
"Tabs" => {
|
||||||
|
new_widget_id = Some(parse_component_tabs(
|
||||||
|
file, ctx, child_node, parent_id, &attribs, tag_name,
|
||||||
|
)?);
|
||||||
|
}
|
||||||
"" => { /* ignore */ }
|
"" => { /* ignore */ }
|
||||||
other_tag_name => {
|
other_tag_name => {
|
||||||
parse_widget_other(other_tag_name, file, ctx, parent_id, &attribs)?;
|
parse_widget_other(other_tag_name, file, ctx, parent_id, &attribs)?;
|
||||||
@@ -1283,3 +1298,22 @@ fn parse_document_root(
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn get_asset_path_from_kv<'a>(prefix: &'static str, key: &'a str, value: &'a str) -> AssetPath<'a> {
|
||||||
|
let key_split = match key.find(prefix) {
|
||||||
|
Some(pos) => {
|
||||||
|
assert!(pos == 0, "invalid split");
|
||||||
|
key.get(prefix.len()..).unwrap()
|
||||||
|
}
|
||||||
|
None => key,
|
||||||
|
};
|
||||||
|
match key_split {
|
||||||
|
"src" => AssetPath::FileOrBuiltIn(value),
|
||||||
|
"src_ext" => AssetPath::File(value),
|
||||||
|
"src_builtin" => AssetPath::BuiltIn(value),
|
||||||
|
"src_internal" => AssetPath::WguiInternal(value),
|
||||||
|
other => {
|
||||||
|
panic!("unexpected attrib {other}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -122,7 +122,6 @@ pub fn parse_text_style(ctx: &ParserContext<'_>, attribs: &[AttribPair], tag_nam
|
|||||||
style
|
style
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
#[allow(clippy::cognitive_complexity)]
|
#[allow(clippy::cognitive_complexity)]
|
||||||
pub fn parse_style(ctx: &ParserContext<'_>, attribs: &[AttribPair], tag_name: &str) -> taffy::Style {
|
pub fn parse_style(ctx: &ParserContext<'_>, attribs: &[AttribPair], tag_name: &str) -> taffy::Style {
|
||||||
let mut style = taffy::Style::default();
|
let mut style = taffy::Style::default();
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ use crate::{
|
|||||||
assets::AssetPath,
|
assets::AssetPath,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{
|
parser::{
|
||||||
AttribPair, ParserContext, ParserFile, parse_children, parse_widget_universal,
|
AttribPair, ParserContext, ParserFile, get_asset_path_from_kv, parse_children, parse_widget_universal,
|
||||||
style::{parse_color, parse_round, parse_style},
|
style::{parse_color, parse_round, parse_style},
|
||||||
},
|
},
|
||||||
renderer_vk::text::custom_glyph::CustomGlyphData,
|
renderer_vk::text::custom_glyph::CustomGlyphData,
|
||||||
@@ -25,16 +25,9 @@ pub fn parse_widget_image<'a>(
|
|||||||
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
|
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
|
||||||
match key {
|
match key {
|
||||||
"src" | "src_ext" | "src_builtin" | "src_internal" => {
|
"src" | "src_ext" | "src_builtin" | "src_internal" => {
|
||||||
let asset_path = match key {
|
|
||||||
"src" => AssetPath::FileOrBuiltIn(value),
|
|
||||||
"src_ext" => AssetPath::File(value),
|
|
||||||
"src_builtin" => AssetPath::BuiltIn(value),
|
|
||||||
"src_internal" => AssetPath::WguiInternal(value),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if !value.is_empty() {
|
if !value.is_empty() {
|
||||||
glyph = match CustomGlyphData::from_assets(&mut ctx.layout.state.globals, asset_path) {
|
glyph = match CustomGlyphData::from_assets(&ctx.layout.state.globals, get_asset_path_from_kv("", key, value))
|
||||||
|
{
|
||||||
Ok(glyph) => Some(glyph),
|
Ok(glyph) => Some(glyph),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
log::warn!("failed to load {value}: {e}");
|
log::warn!("failed to load {value}: {e}");
|
||||||
|
|||||||
@@ -1,7 +1,10 @@
|
|||||||
use crate::{
|
use crate::{
|
||||||
assets::AssetPath,
|
assets::AssetPath,
|
||||||
layout::WidgetID,
|
layout::WidgetID,
|
||||||
parser::{AttribPair, ParserContext, ParserFile, parse_children, parse_widget_universal, style::parse_style},
|
parser::{
|
||||||
|
AttribPair, ParserContext, ParserFile, get_asset_path_from_kv, parse_children, parse_widget_universal,
|
||||||
|
style::parse_style,
|
||||||
|
},
|
||||||
renderer_vk::text::custom_glyph::CustomGlyphData,
|
renderer_vk::text::custom_glyph::CustomGlyphData,
|
||||||
widget::sprite::{WidgetSprite, WidgetSpriteParams},
|
widget::sprite::{WidgetSprite, WidgetSpriteParams},
|
||||||
};
|
};
|
||||||
@@ -24,13 +27,7 @@ pub fn parse_widget_sprite<'a>(
|
|||||||
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
|
let (key, value) = (pair.attrib.as_ref(), pair.value.as_ref());
|
||||||
match key {
|
match key {
|
||||||
"src" | "src_ext" | "src_builtin" | "src_internal" => {
|
"src" | "src_ext" | "src_builtin" | "src_internal" => {
|
||||||
let asset_path = match key {
|
let asset_path = get_asset_path_from_kv("", key, value);
|
||||||
"src" => AssetPath::FileOrBuiltIn(value),
|
|
||||||
"src_ext" => AssetPath::File(value),
|
|
||||||
"src_builtin" => AssetPath::BuiltIn(value),
|
|
||||||
"src_internal" => AssetPath::WguiInternal(value),
|
|
||||||
_ => unreachable!(),
|
|
||||||
};
|
|
||||||
|
|
||||||
if !value.is_empty() {
|
if !value.is_empty() {
|
||||||
glyph = match CustomGlyphData::from_assets(&ctx.layout.state.globals, asset_path) {
|
glyph = match CustomGlyphData::from_assets(&ctx.layout.state.globals, asset_path) {
|
||||||
|
|||||||
@@ -54,7 +54,6 @@ impl TextRenderer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Prepares all of the provided text areas for rendering.
|
/// Prepares all of the provided text areas for rendering.
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
pub fn prepare<'a>(
|
pub fn prepare<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
font_system: &mut FontSystem,
|
font_system: &mut FontSystem,
|
||||||
@@ -324,7 +323,6 @@ struct PrepareGlyphParams<'a> {
|
|||||||
bounds_max_y: i32,
|
bounds_max_y: i32,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
fn prepare_glyph(
|
fn prepare_glyph(
|
||||||
par: &mut PrepareGlyphParams,
|
par: &mut PrepareGlyphParams,
|
||||||
get_glyph_image: impl FnOnce(&mut SwashCache, &mut FontSystem) -> Option<GetGlyphImageResult>,
|
get_glyph_image: impl FnOnce(&mut SwashCache, &mut FontSystem) -> Option<GetGlyphImageResult>,
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ impl<TaskType: Clone + 'static> Tasks<TaskType> {
|
|||||||
pub fn handle_button(&self, button: &Rc<ComponentButton>, task: TaskType) {
|
pub fn handle_button(&self, button: &Rc<ComponentButton>, task: TaskType) {
|
||||||
button.on_click({
|
button.on_click({
|
||||||
let this = self.clone();
|
let this = self.clone();
|
||||||
Box::new(move |_, _| {
|
Rc::new(move |_, _| {
|
||||||
this.push(task.clone());
|
this.push(task.clone());
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -100,7 +100,6 @@ impl WguiWindow {
|
|||||||
self.0.borrow_mut().opened_window = None;
|
self.0.borrow_mut().opened_window = None;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_lines)]
|
|
||||||
pub fn open(&mut self, params: &mut WguiWindowParams) -> anyhow::Result<()> {
|
pub fn open(&mut self, params: &mut WguiWindowParams) -> anyhow::Result<()> {
|
||||||
// close previous one if it's already open
|
// close previous one if it's already open
|
||||||
self.close();
|
self.close();
|
||||||
@@ -233,7 +232,7 @@ impl WguiWindow {
|
|||||||
let but_close = state.fetch_component_as::<ComponentButton>("but_close").unwrap();
|
let but_close = state.fetch_component_as::<ComponentButton>("but_close").unwrap();
|
||||||
but_close.on_click({
|
but_close.on_click({
|
||||||
let this = self.clone();
|
let this = self.clone();
|
||||||
Box::new(move |_common, _e| {
|
Rc::new(move |_common, _e| {
|
||||||
this.close();
|
this.close();
|
||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user