dash-frontend: toast messages, 📦📎
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -1432,6 +1432,7 @@ dependencies = [
|
||||
"serde",
|
||||
"serde_json",
|
||||
"wgui",
|
||||
"wlx-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
||||
@@ -22,7 +22,7 @@ resolver = "3"
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0.100"
|
||||
glam = "0.30.7"
|
||||
glam = { version = "0.30.7", features = ["mint", "serde"] }
|
||||
idmap = "0.2.2"
|
||||
idmap-derive = "0.2.2"
|
||||
log = "0.4.28"
|
||||
|
||||
@@ -6,7 +6,7 @@ edition = "2024"
|
||||
[dependencies]
|
||||
anyhow.workspace = true
|
||||
wgui = { path = "../wgui/" }
|
||||
glam = { workspace = true }
|
||||
glam = { workspace = true, features = ["mint", "serde"] }
|
||||
log = { workspace = true }
|
||||
rust-embed = { workspace = true }
|
||||
chrono = "0.4.42"
|
||||
@@ -14,3 +14,4 @@ gio = "0.21.2"
|
||||
gtk = "0.18.2"
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde_json = "1.0.145"
|
||||
wlx-common = { path = "../wlx-common" }
|
||||
|
||||
@@ -98,7 +98,6 @@
|
||||
</div>
|
||||
|
||||
<div position="absolute" id="popup_manager" width="100%" height="100%" />
|
||||
|
||||
</rectangle>
|
||||
<!-- BOTTOM PANEL -->
|
||||
<rectangle
|
||||
|
||||
@@ -35,7 +35,13 @@
|
||||
"SPEAKERS": "Lautsprecher",
|
||||
"MICROPHONES": "Mikrofone",
|
||||
"CARDS": "Karten",
|
||||
"SELECT_AUDIO_CARD_PROFILE": "Wählen Sie das Audio-Kartenprofil"
|
||||
"SELECT_AUDIO_CARD_PROFILE": "Wählen Sie das Audio-Kartenprofil",
|
||||
"NO_VR_SPEAKERS_FOUND_SWITCH_MANUALLY": "Keine VR-Lautsprecher gefunden. Schalten Sie diese manuell um.",
|
||||
"NO_VR_MICROPHONE_SWITCH_MANUALLY": "Kein VR-Mikrofon gefunden. Schalten Sie es manuell um.",
|
||||
"FAILED_TO_SWITCH_MICROPHONE": "Fehler beim Wechseln des Mikrofons",
|
||||
"MICROPHONE_SET_SUCCESSFULLY": "Mikrofon erfolgreich eingestellt",
|
||||
"SPEAKERS_SET_SUCCESSFULLY": "Lautsprecher erfolgreich eingestellt",
|
||||
"DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED": "Gerät gefunden und initialisiert, aber nicht umgeschaltet"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"RECENTER_PLAYSPACE": "Playspace neu zentrieren"
|
||||
|
||||
@@ -35,7 +35,13 @@
|
||||
"AUTO_SWITCH_TO_VR_AUDIO": "Auto-switch to VR audio",
|
||||
"SPEAKERS": "Speakers",
|
||||
"MICROPHONES": "Microphones",
|
||||
"CARDS": "Cards"
|
||||
"CARDS": "Cards",
|
||||
"NO_VR_SPEAKERS_FOUND_SWITCH_MANUALLY": "No VR speakers found. Switch them manually.",
|
||||
"NO_VR_MICROPHONE_SWITCH_MANUALLY": "No VR microphone found. Switch it manually.",
|
||||
"FAILED_TO_SWITCH_MICROPHONE": "Failed to switch microphone",
|
||||
"MICROPHONE_SET_SUCCESSFULLY": "Microphone set successfully",
|
||||
"SPEAKERS_SET_SUCCESSFULLY": "Speakers set successfully",
|
||||
"DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED": "Device found and initialized, but not switched"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"RECENTER_PLAYSPACE": "Re-center playspace"
|
||||
|
||||
@@ -35,7 +35,13 @@
|
||||
"SPEAKERS": "Altavoces",
|
||||
"MICROPHONES": "Micrófonos",
|
||||
"CARDS": "Tarjetas",
|
||||
"SELECT_AUDIO_CARD_PROFILE": "Seleccionar perfil de tarjeta de audio"
|
||||
"SELECT_AUDIO_CARD_PROFILE": "Seleccionar perfil de tarjeta de audio",
|
||||
"NO_VR_SPEAKERS_FOUND_SWITCH_MANUALLY": "No se encontraron altavoces VR. Actívelos manualmente.",
|
||||
"NO_VR_MICROPHONE_SWITCH_MANUALLY": "No se encontró micrófono VR. Actívelo manualmente.",
|
||||
"FAILED_TO_SWITCH_MICROPHONE": "No se pudo cambiar el micrófono",
|
||||
"MICROPHONE_SET_SUCCESSFULLY": "Micrófono configurado correctamente",
|
||||
"SPEAKERS_SET_SUCCESSFULLY": "Altavoces configurados correctamente",
|
||||
"DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED": "Dispositivo encontrado e inicializado, pero no cambiado"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"RECENTER_PLAYSPACE": "Re-centrar espacio de juego"
|
||||
|
||||
@@ -35,7 +35,13 @@
|
||||
"SPEAKERS": "スピーカー",
|
||||
"MICROPHONES": "マイク",
|
||||
"CARDS": "カード",
|
||||
"SELECT_AUDIO_CARD_PROFILE": "オーディオカードプロファイルを選択"
|
||||
"SELECT_AUDIO_CARD_PROFILE": "オーディオカードプロファイルを選択",
|
||||
"NO_VR_SPEAKERS_FOUND_SWITCH_MANUALLY": "VRスピーカーが見つかりませんでした。手動で切り替えてください。",
|
||||
"NO_VR_MICROPHONE_SWITCH_MANUALLY": "VRマイクが見つかりませんでした。手動で切り替えてください。",
|
||||
"FAILED_TO_SWITCH_MICROPHONE": "マイクの切り替えに失敗しました",
|
||||
"MICROPHONE_SET_SUCCESSFULLY": "マイクの設定が完了しました",
|
||||
"SPEAKERS_SET_SUCCESSFULLY": "スピーカーを設定しました",
|
||||
"DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED": "デバイスが見つかり、初期化されましたが、切り替えられていません"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"RECENTER_PLAYSPACE": "プレイスペースを再中央"
|
||||
|
||||
@@ -35,7 +35,13 @@
|
||||
"SPEAKERS": "Głośniki",
|
||||
"MICROPHONES": "Mikrofony",
|
||||
"CARDS": "Karty",
|
||||
"SELECT_AUDIO_CARD_PROFILE": "Wybierz profil karty dźwiękowej"
|
||||
"SELECT_AUDIO_CARD_PROFILE": "Wybierz profil karty dźwiękowej",
|
||||
"NO_VR_SPEAKERS_FOUND_SWITCH_MANUALLY": "Brak głośników VR. Włącz je ręcznie.",
|
||||
"NO_VR_MICROPHONE_SWITCH_MANUALLY": "Brak mikrofonu VR. Włącz go ręcznie.",
|
||||
"FAILED_TO_SWITCH_MICROPHONE": "Nie udało się przełączyć mikrofon",
|
||||
"MICROPHONE_SET_SUCCESSFULLY": "Mikrofon ustawiono pomyślnie",
|
||||
"SPEAKERS_SET_SUCCESSFULLY": "Głośniki ustawiono pomyślnie",
|
||||
"DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED": "Urządzenie znalezione i zainicjalizowane, ale nie przełączone"
|
||||
},
|
||||
"ACTIONS": {
|
||||
"RECENTER_PLAYSPACE": "Wycentruj przestrzeń"
|
||||
|
||||
@@ -13,6 +13,7 @@ use wgui::{
|
||||
widget::{label::WidgetLabel, rectangle::WidgetRectangle},
|
||||
windowing::{WguiWindow, WguiWindowParams, WguiWindowParamsExtra, WguiWindowPlacement},
|
||||
};
|
||||
use wlx_common::timestep::Timestep;
|
||||
|
||||
use crate::{
|
||||
assets, settings,
|
||||
@@ -21,7 +22,10 @@ use crate::{
|
||||
settings::TabSettings,
|
||||
},
|
||||
task::Tasks,
|
||||
util::popup_manager::{MountPopupParams, PopupManager, PopupManagerParams},
|
||||
util::{
|
||||
popup_manager::{MountPopupParams, PopupManager, PopupManagerParams},
|
||||
toast_manager::ToastManager,
|
||||
},
|
||||
views,
|
||||
};
|
||||
|
||||
@@ -49,6 +53,8 @@ pub struct Frontend {
|
||||
|
||||
widgets: FrontendWidgets,
|
||||
popup_manager: PopupManager,
|
||||
toast_manager: ToastManager,
|
||||
timestep: Timestep,
|
||||
|
||||
window_audio_settings: WguiWindow,
|
||||
view_audio_settings: Option<views::audio_settings::View>,
|
||||
@@ -70,7 +76,7 @@ pub enum FrontendTask {
|
||||
ShowAudioSettings,
|
||||
UpdateAudioSettingsView,
|
||||
RecenterPlayspace,
|
||||
PushToast(String),
|
||||
PushToast(Translation),
|
||||
}
|
||||
|
||||
impl Frontend {
|
||||
@@ -92,7 +98,7 @@ impl Frontend {
|
||||
},
|
||||
)?;
|
||||
|
||||
let (mut layout, state) = wgui::parser::new_layout_from_assets(
|
||||
let (layout, state) = wgui::parser::new_layout_from_assets(
|
||||
&ParseDocumentParams {
|
||||
globals: globals.clone(),
|
||||
path: AssetPath::BuiltIn("gui/dashboard.xml"),
|
||||
@@ -103,10 +109,10 @@ impl Frontend {
|
||||
|
||||
let id_popup_manager = state.get_widget_id("popup_manager")?;
|
||||
let popup_manager = PopupManager::new(PopupManagerParams {
|
||||
globals: globals.clone(),
|
||||
layout: &mut layout,
|
||||
parent_id: id_popup_manager,
|
||||
})?;
|
||||
});
|
||||
|
||||
let toast_manager = ToastManager::new();
|
||||
|
||||
let rc_layout = layout.as_rc();
|
||||
|
||||
@@ -116,6 +122,9 @@ impl Frontend {
|
||||
let id_label_time = state.get_widget_id("label_time")?;
|
||||
let id_rect_content = state.get_widget_id("rect_content")?;
|
||||
|
||||
let mut timestep = Timestep::new();
|
||||
timestep.set_tps(30.0); // 30 ticks per second
|
||||
|
||||
let frontend = Self {
|
||||
layout: rc_layout.clone(),
|
||||
state,
|
||||
@@ -127,8 +136,10 @@ impl Frontend {
|
||||
id_label_time,
|
||||
id_rect_content,
|
||||
},
|
||||
timestep,
|
||||
settings: params.settings,
|
||||
popup_manager,
|
||||
toast_manager,
|
||||
window_audio_settings: WguiWindow::default(),
|
||||
view_audio_settings: None,
|
||||
};
|
||||
@@ -165,6 +176,12 @@ impl Frontend {
|
||||
|
||||
{
|
||||
let mut layout = self.layout.borrow_mut();
|
||||
|
||||
// always 30 times per second
|
||||
while self.timestep.on_tick() {
|
||||
self.toast_manager.tick(&self.globals, &mut layout)?;
|
||||
}
|
||||
|
||||
layout.update(Vec2::new(width, height), timestep_alpha)?;
|
||||
}
|
||||
|
||||
@@ -253,8 +270,8 @@ impl Frontend {
|
||||
FrontendTask::ShowAudioSettings => self.action_show_audio_settings()?,
|
||||
FrontendTask::UpdateAudioSettingsView => self.action_update_audio_settings()?,
|
||||
FrontendTask::RecenterPlayspace => self.action_recenter_playspace()?,
|
||||
FrontendTask::PushToast(text) => self.push_toast(text)?,
|
||||
}
|
||||
FrontendTask::PushToast(content) => self.toast_manager.push(content),
|
||||
};
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -269,7 +286,7 @@ impl Frontend {
|
||||
layout: &mut layout,
|
||||
parent_id: widget_content.id,
|
||||
frontend: rc_this,
|
||||
frontend_widgets: &self.widgets,
|
||||
//frontend_widgets: &self.widgets,
|
||||
settings: self.settings.get_mut(),
|
||||
};
|
||||
|
||||
@@ -413,9 +430,4 @@ impl Frontend {
|
||||
log::info!("todo");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn push_toast(&mut self, text: String) -> anyhow::Result<()> {
|
||||
log::info!("TODO toast: {}", text);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ use wgui::{
|
||||
globals::WguiGlobals,
|
||||
i18n::Translation,
|
||||
layout::WidgetPair,
|
||||
parser::{Fetchable, ParseDocumentParams, ParserData, ParserState},
|
||||
parser::{Fetchable, ParseDocumentParams, ParserState},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
@@ -28,6 +28,7 @@ pub struct TabApps {
|
||||
#[allow(dead_code)]
|
||||
pub parser_state: ParserState,
|
||||
|
||||
#[allow(dead_code)]
|
||||
state: Rc<RefCell<State>>,
|
||||
|
||||
#[allow(dead_code)]
|
||||
@@ -44,7 +45,7 @@ impl Tab for TabApps {
|
||||
|
||||
#[derive(Default)]
|
||||
struct AppList {
|
||||
data: Vec<ParserData>,
|
||||
//data: Vec<ParserData>,
|
||||
}
|
||||
|
||||
// called after the user clicks any desktop entry
|
||||
|
||||
@@ -1,12 +1,9 @@
|
||||
use std::rc::Rc;
|
||||
|
||||
use wgui::{
|
||||
components::button::ComponentButton,
|
||||
globals::WguiGlobals,
|
||||
layout::{Layout, WidgetID},
|
||||
};
|
||||
|
||||
use crate::frontend::{FrontendTask, FrontendWidgets, RcFrontend};
|
||||
use crate::frontend::RcFrontend;
|
||||
|
||||
pub mod apps;
|
||||
pub mod games;
|
||||
@@ -30,7 +27,6 @@ pub struct TabParams<'a> {
|
||||
pub layout: &'a mut Layout,
|
||||
pub parent_id: WidgetID,
|
||||
pub frontend: &'a RcFrontend,
|
||||
pub frontend_widgets: &'a FrontendWidgets,
|
||||
pub settings: &'a mut crate::settings::Settings,
|
||||
}
|
||||
|
||||
|
||||
@@ -19,3 +19,9 @@ impl<TaskType: Clone + 'static> Tasks<TaskType> {
|
||||
std::mem::take(&mut *tasks)
|
||||
}
|
||||
}
|
||||
|
||||
impl<TaskType: Clone + 'static> Default for Tasks<TaskType> {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
pub mod desktop_finder;
|
||||
pub mod pactl_wrapper;
|
||||
pub mod popup_manager;
|
||||
pub mod toast_manager;
|
||||
|
||||
@@ -17,21 +17,18 @@ use wgui::{
|
||||
|
||||
use crate::frontend::{FrontendTask, FrontendTasks};
|
||||
|
||||
pub struct PopupManagerParams<'a> {
|
||||
pub globals: WguiGlobals,
|
||||
pub layout: &'a mut Layout,
|
||||
pub struct PopupManagerParams {
|
||||
pub parent_id: WidgetID,
|
||||
}
|
||||
|
||||
pub struct State {
|
||||
struct State {
|
||||
popup_stack: Vec<Weak<RefCell<MountedPopupState>>>,
|
||||
}
|
||||
|
||||
pub struct MountedPopup {
|
||||
#[allow(dead_code)]
|
||||
state: ParserState,
|
||||
id_root: WidgetID, // decorations of a popup
|
||||
pub id_content: WidgetID, // content of a popup
|
||||
id_root: WidgetID, // decorations of a popup
|
||||
layout_tasks: LayoutTasks,
|
||||
frontend_tasks: FrontendTasks,
|
||||
}
|
||||
@@ -52,8 +49,7 @@ impl PopupHandle {
|
||||
}
|
||||
|
||||
pub struct PopupManager {
|
||||
pub state: Rc<RefCell<State>>,
|
||||
globals: WguiGlobals,
|
||||
state: Rc<RefCell<State>>,
|
||||
parent_id: WidgetID,
|
||||
}
|
||||
|
||||
@@ -104,14 +100,13 @@ impl State {
|
||||
}
|
||||
|
||||
impl PopupManager {
|
||||
pub fn new(params: PopupManagerParams) -> anyhow::Result<Self> {
|
||||
Ok(Self {
|
||||
globals: params.globals,
|
||||
pub fn new(params: PopupManagerParams) -> Self {
|
||||
Self {
|
||||
parent_id: params.parent_id,
|
||||
state: Rc::new(RefCell::new(State {
|
||||
popup_stack: Vec::new(),
|
||||
})),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refresh(&self, alterables: &mut EventAlterables) {
|
||||
@@ -147,7 +142,6 @@ impl PopupManager {
|
||||
|
||||
let mounted_popup = MountedPopup {
|
||||
state,
|
||||
id_content,
|
||||
id_root,
|
||||
layout_tasks: layout.tasks.clone(),
|
||||
frontend_tasks: frontend_tasks.clone(),
|
||||
|
||||
190
dash-frontend/src/util/toast_manager.rs
Normal file
190
dash-frontend/src/util/toast_manager.rs
Normal file
@@ -0,0 +1,190 @@
|
||||
use std::{cell::RefCell, collections::VecDeque, rc::Rc};
|
||||
|
||||
use glam::{Mat4, Vec3};
|
||||
use wgui::{
|
||||
animation::{Animation, AnimationEasing},
|
||||
components::tooltip::{TOOLTIP_BORDER_COLOR, TOOLTIP_COLOR},
|
||||
drawing::Color,
|
||||
globals::WguiGlobals,
|
||||
i18n::Translation,
|
||||
layout::{Layout, LayoutTask, LayoutTasks, WidgetID},
|
||||
renderer_vk::{
|
||||
text::{FontWeight, TextStyle},
|
||||
util::centered_matrix,
|
||||
},
|
||||
taffy::{
|
||||
self,
|
||||
prelude::{length, percent},
|
||||
},
|
||||
widget::{
|
||||
div::WidgetDiv,
|
||||
label::{WidgetLabel, WidgetLabelParams},
|
||||
rectangle::{WidgetRectangle, WidgetRectangleParams},
|
||||
util::WLength,
|
||||
},
|
||||
};
|
||||
|
||||
struct MountedToast {
|
||||
#[allow(dead_code)]
|
||||
id_root: WidgetID, // decorations of a toast
|
||||
layout_tasks: LayoutTasks,
|
||||
}
|
||||
|
||||
struct State {
|
||||
toast: Option<MountedToast>,
|
||||
queue: VecDeque<Translation>,
|
||||
timeout: u32, // in ticks
|
||||
}
|
||||
|
||||
pub struct ToastManager {
|
||||
state: Rc<RefCell<State>>,
|
||||
needs_tick: bool,
|
||||
}
|
||||
|
||||
impl Drop for MountedToast {
|
||||
fn drop(&mut self) {
|
||||
self.layout_tasks.push(LayoutTask::RemoveWidget(self.id_root));
|
||||
}
|
||||
}
|
||||
|
||||
const TOAST_DURATION_TICKS: u32 = 90;
|
||||
|
||||
impl ToastManager {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
state: Rc::new(RefCell::new(State {
|
||||
toast: None,
|
||||
timeout: 0,
|
||||
queue: VecDeque::new(),
|
||||
})),
|
||||
needs_tick: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn mount_toast(
|
||||
&self,
|
||||
globals: &WguiGlobals,
|
||||
layout: &mut Layout,
|
||||
state: &mut State,
|
||||
content: Translation,
|
||||
) -> anyhow::Result<()> {
|
||||
let mut globals = globals.get();
|
||||
|
||||
let (root, _) = layout.add_topmost_child(
|
||||
WidgetDiv::create(),
|
||||
taffy::Style {
|
||||
position: taffy::Position::Absolute,
|
||||
size: taffy::Size {
|
||||
width: percent(1.0),
|
||||
height: percent(0.8),
|
||||
},
|
||||
align_items: Some(taffy::AlignItems::End),
|
||||
justify_content: Some(taffy::JustifyContent::Center),
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
let (rect, _) = layout.add_child(
|
||||
root.id,
|
||||
WidgetRectangle::create(WidgetRectangleParams {
|
||||
color: TOOLTIP_COLOR,
|
||||
border_color: TOOLTIP_BORDER_COLOR,
|
||||
border: 2.0,
|
||||
round: WLength::Percent(1.0),
|
||||
..Default::default()
|
||||
}),
|
||||
taffy::Style {
|
||||
position: taffy::Position::Relative,
|
||||
gap: length(4.0),
|
||||
padding: taffy::Rect {
|
||||
left: length(16.0),
|
||||
right: length(16.0),
|
||||
top: length(8.0),
|
||||
bottom: length(8.0),
|
||||
},
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
let (label, _) = layout.add_child(
|
||||
rect.id,
|
||||
WidgetLabel::create(
|
||||
&mut globals,
|
||||
WidgetLabelParams {
|
||||
content,
|
||||
style: TextStyle {
|
||||
weight: Some(FontWeight::Bold),
|
||||
..Default::default()
|
||||
},
|
||||
},
|
||||
),
|
||||
taffy::Style { ..Default::default() },
|
||||
)?;
|
||||
|
||||
// show-up animation
|
||||
layout.animations.add(Animation::new(
|
||||
rect.id,
|
||||
160,
|
||||
AnimationEasing::Linear,
|
||||
Box::new(move |common, data| {
|
||||
let pos_showup = AnimationEasing::OutQuint.interpolate((data.pos * 4.0).min(1.0));
|
||||
let opacity = 1.0 - AnimationEasing::OutQuint.interpolate(((data.pos - 0.75) * 4.0).clamp(0.0, 1.0));
|
||||
let scale = AnimationEasing::OutBack.interpolate((data.pos * 4.0).min(1.0));
|
||||
|
||||
{
|
||||
let mtx = Mat4::from_translation(Vec3::new(0.0, (1.0 - pos_showup) * 100.0, 0.0))
|
||||
* Mat4::from_scale(Vec3::new(scale, scale, 1.0));
|
||||
data.data.transform = centered_matrix(data.widget_boundary.size, &mtx);
|
||||
}
|
||||
|
||||
let rect = data.obj.get_as_mut::<WidgetRectangle>().unwrap();
|
||||
rect.params.color.a = opacity;
|
||||
rect.params.border_color.a = opacity;
|
||||
|
||||
let mut label = common.state.widgets.get_as::<WidgetLabel>(label.id).unwrap();
|
||||
label.set_color(common, Color::new(1.0, 1.0, 1.0, opacity), true);
|
||||
common.alterables.mark_redraw();
|
||||
}),
|
||||
));
|
||||
|
||||
state.toast = Some(MountedToast {
|
||||
id_root: root.id,
|
||||
layout_tasks: layout.tasks.clone(),
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn tick(&mut self, globals: &WguiGlobals, layout: &mut Layout) -> anyhow::Result<()> {
|
||||
if !self.needs_tick {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut state = self.state.borrow_mut();
|
||||
|
||||
if state.timeout > 0 {
|
||||
state.timeout -= 1;
|
||||
}
|
||||
|
||||
if state.timeout == 0 {
|
||||
state.toast = None;
|
||||
state.timeout = TOAST_DURATION_TICKS;
|
||||
// mount next
|
||||
if let Some(content) = state.queue.pop_front() {
|
||||
self.mount_toast(globals, layout, &mut state, content)?;
|
||||
}
|
||||
}
|
||||
|
||||
if state.queue.is_empty() && state.toast.is_none() {
|
||||
self.needs_tick = false;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn push(&mut self, content: Translation) {
|
||||
let mut state = self.state.borrow_mut();
|
||||
state.queue.push_back(content);
|
||||
self.needs_tick = true;
|
||||
}
|
||||
}
|
||||
@@ -424,14 +424,12 @@ fn switch_sink_card(
|
||||
}
|
||||
|
||||
if sink_found {
|
||||
frontend_tasks.push(FrontendTask::PushToast(format!(
|
||||
"Speakers set to \"{}\" successfully!",
|
||||
name.name
|
||||
frontend_tasks.push(FrontendTask::PushToast(Translation::from_translation_key(
|
||||
format!("[AUDIO.SPEAKERS_SET_SUCCESSFULLY]: {}", name.name).as_str(),
|
||||
)));
|
||||
} else {
|
||||
frontend_tasks.push(FrontendTask::PushToast(format!(
|
||||
"\"{}\" found and initialized! (not switched)",
|
||||
name.name
|
||||
frontend_tasks.push(FrontendTask::PushToast(Translation::from_translation_key(
|
||||
format!("[AUDIO.DEVICE_FOUND_AND_INITIALIZED_BUT_NOT_SWITCHED]: {}", name.name).as_str(),
|
||||
)));
|
||||
}
|
||||
|
||||
@@ -441,18 +439,21 @@ fn switch_sink_card(
|
||||
fn switch_source(frontend_tasks: &FrontendTasks, source: &pactl_wrapper::Source) -> anyhow::Result<()> {
|
||||
match pactl_wrapper::set_default_source(source.index) {
|
||||
Ok(()) => {
|
||||
frontend_tasks.push(FrontendTask::PushToast(format!(
|
||||
"Microphone set to \"{}\" successfully!",
|
||||
if let Some(card_name) = &source.properties.card_name {
|
||||
card_name
|
||||
} else {
|
||||
&source.description
|
||||
}
|
||||
frontend_tasks.push(FrontendTask::PushToast(Translation::from_translation_key(
|
||||
format!(
|
||||
"[AUDIO.MICROPHONE_SET_SUCCESSFULLY]: {}",
|
||||
if let Some(card_name) = &source.properties.card_name {
|
||||
card_name
|
||||
} else {
|
||||
&source.description
|
||||
}
|
||||
)
|
||||
.as_str(),
|
||||
)));
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
frontend_tasks.push(FrontendTask::PushToast(format!("Failed to switch microphone: {:?}", e)));
|
||||
Translation::from_translation_key(format!("[AUDIO.FAILED_TO_SWITCH_MICROPHONE]: {:?}", e).as_str());
|
||||
Err(e)
|
||||
}
|
||||
}
|
||||
@@ -471,9 +472,9 @@ fn switch_to_vr_microphone(frontend_tasks: &FrontendTasks) -> anyhow::Result<()>
|
||||
}
|
||||
|
||||
if !switched {
|
||||
frontend_tasks.push(FrontendTask::PushToast(
|
||||
"No VR microphone found. Switch it manually.".to_string(),
|
||||
));
|
||||
frontend_tasks.push(FrontendTask::PushToast(Translation::from_translation_key(
|
||||
"AUDIO.NO_VR_MICROPHONE_SWITCH_MANUALLY",
|
||||
)));
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@@ -560,9 +561,9 @@ fn switch_to_vr_speakers(frontend_tasks: &FrontendTasks) -> anyhow::Result<()> {
|
||||
}
|
||||
}
|
||||
|
||||
frontend_tasks.push(FrontendTask::PushToast(
|
||||
"No VR speakers found. Switch them manually.".to_string(),
|
||||
));
|
||||
frontend_tasks.push(FrontendTask::PushToast(Translation::from_translation_key(
|
||||
"AUDIO.NO_VR_SPEAKERS_FOUND_SWITCH_MANUALLY",
|
||||
)));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ pub enum AnimationEasing {
|
||||
}
|
||||
|
||||
impl AnimationEasing {
|
||||
fn interpolate(&self, x: f32) -> f32 {
|
||||
pub fn interpolate(&self, x: f32) -> f32 {
|
||||
match self {
|
||||
Self::Linear => x,
|
||||
Self::InQuad => x.powi(2),
|
||||
|
||||
@@ -95,7 +95,7 @@ pub fn normalize_path(path: &Path) -> PathBuf {
|
||||
Component::ParentDir => {
|
||||
match stack.last() {
|
||||
// ../foo, ../../foo, ./../foo → push ".."
|
||||
None | Some(Component::ParentDir) | Some(Component::CurDir) => stack.push(Component::ParentDir),
|
||||
None | Some(Component::ParentDir | Component::CurDir) => stack.push(Component::ParentDir),
|
||||
// "foo/../bar" → pop "foo" and don't push ".."
|
||||
Some(Component::Normal(_)) => {
|
||||
stack.pop();
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
use taffy::{
|
||||
AlignItems, JustifyContent,
|
||||
AlignItems,
|
||||
prelude::{length, percent},
|
||||
};
|
||||
|
||||
|
||||
@@ -86,6 +86,9 @@ impl Drop for ComponentTooltip {
|
||||
}
|
||||
}
|
||||
|
||||
pub const TOOLTIP_COLOR: Color = Color::new(0.1, 0.1, 0.1, 0.9);
|
||||
pub const TOOLTIP_BORDER_COLOR: Color = Color::new(0.3, 0.3, 0.3, 1.0);
|
||||
|
||||
#[allow(clippy::too_many_lines)]
|
||||
pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Result<(WidgetPair, Rc<ComponentTooltip>)> {
|
||||
let absolute_boundary = {
|
||||
@@ -103,7 +106,7 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
||||
|
||||
let transform = Mat4::from_translation(Vec3::new(-0.5, 0.0, 0.0));
|
||||
|
||||
let (mut pin_left, mut pin_top, pin_align_items, pin_justify_content) = match params.info.side {
|
||||
let (pin_left, pin_top, pin_align_items, pin_justify_content) = match params.info.side {
|
||||
TooltipSide::Left => (
|
||||
absolute_boundary.left() - spacing,
|
||||
absolute_boundary.top() + absolute_boundary.size.y / 2.0,
|
||||
@@ -159,8 +162,8 @@ pub fn construct(ess: &mut ConstructEssentials, params: Params) -> anyhow::Resul
|
||||
let (rect, _) = ess.layout.add_child(
|
||||
div.id,
|
||||
WidgetRectangle::create(WidgetRectangleParams {
|
||||
color: Color::new(0.1, 0.1, 0.1, 0.8),
|
||||
border_color: Color::new(0.3, 0.3, 0.3, 1.0),
|
||||
color: TOOLTIP_COLOR,
|
||||
border_color: TOOLTIP_BORDER_COLOR,
|
||||
border: 2.0,
|
||||
round: WLength::Percent(1.0),
|
||||
..Default::default()
|
||||
|
||||
@@ -5,7 +5,7 @@ edition = "2024"
|
||||
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0.228", features = ["derive"] }
|
||||
serde = { version = "1.0.228", features = ["derive", "rc"] }
|
||||
glam = { workspace = true }
|
||||
chrono = "0.4.42"
|
||||
idmap = { workspace = true, features = ["serde"] }
|
||||
|
||||
@@ -2,4 +2,5 @@ pub mod astr_containers;
|
||||
pub mod common;
|
||||
pub mod config;
|
||||
pub mod overlays;
|
||||
pub mod timestep;
|
||||
pub mod windowing;
|
||||
|
||||
@@ -52,7 +52,7 @@ rodio = { version = "0.21.1", default-features = false, features = [
|
||||
"hound",
|
||||
] }
|
||||
rosc = { version = "0.11.4", optional = true }
|
||||
serde = { version = "1.0.225", features = ["derive", "rc"] }
|
||||
serde = { version = "1.0.228", features = ["derive", "rc"] }
|
||||
serde_json = "1.0.145"
|
||||
serde_yaml = "0.9.34"
|
||||
slotmap = { workspace = true }
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
pub mod asset;
|
||||
pub mod panel;
|
||||
pub mod timer;
|
||||
mod timestep;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use button::setup_custom_button;
|
||||
use glam::{vec2, Affine2, Vec2};
|
||||
use glam::{Affine2, Vec2, vec2};
|
||||
use label::setup_custom_label;
|
||||
use wgui::{
|
||||
assets::AssetPath,
|
||||
@@ -15,19 +15,20 @@ use wgui::{
|
||||
layout::{Layout, LayoutParams, WidgetID},
|
||||
parser::{CustomAttribsInfoOwned, ParserState},
|
||||
renderer_vk::context::Context as WguiContext,
|
||||
widget::{label::WidgetLabel, rectangle::WidgetRectangle, EventResult},
|
||||
widget::{EventResult, label::WidgetLabel, rectangle::WidgetRectangle},
|
||||
};
|
||||
use wlx_common::timestep::Timestep;
|
||||
|
||||
use crate::{
|
||||
backend::input::{Haptics, HoverResult, PointerHit, PointerMode},
|
||||
state::AppState,
|
||||
subsystem::hid::WheelDelta,
|
||||
windowing::backend::{
|
||||
ui_transform, FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender,
|
||||
FrameMeta, OverlayBackend, OverlayEventData, RenderResources, ShouldRender, ui_transform,
|
||||
},
|
||||
};
|
||||
|
||||
use super::{timer::GuiTimer, timestep::Timestep};
|
||||
use super::timer::GuiTimer;
|
||||
|
||||
pub mod button;
|
||||
mod helper;
|
||||
|
||||
@@ -14,7 +14,7 @@ use wgui::{
|
||||
parser::Fetchable,
|
||||
renderer_vk::text::custom_glyph::CustomGlyphData,
|
||||
taffy,
|
||||
widget::{sprite::WidgetSprite, EventResult},
|
||||
widget::{EventResult, sprite::WidgetSprite},
|
||||
};
|
||||
use wlx_common::windowing::{OverlayWindowState, Positioning};
|
||||
|
||||
@@ -24,16 +24,16 @@ use crate::{
|
||||
task::{ManagerTask, TaskType},
|
||||
},
|
||||
gui::{
|
||||
panel::{button::BUTTON_EVENTS, GuiPanel, NewGuiPanelParams, OnCustomAttribFunc},
|
||||
panel::{GuiPanel, NewGuiPanelParams, OnCustomAttribFunc, button::BUTTON_EVENTS},
|
||||
timer::GuiTimer,
|
||||
},
|
||||
overlays::edit::LongPressButtonState,
|
||||
state::AppState,
|
||||
windowing::{
|
||||
OverlaySelector, Z_ORDER_WATCH,
|
||||
backend::{OverlayEventData, OverlayMeta},
|
||||
manager::MAX_OVERLAY_SETS,
|
||||
window::{OverlayWindowConfig, OverlayWindowData},
|
||||
OverlaySelector, Z_ORDER_WATCH,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -205,7 +205,7 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
if idx >= num_children {
|
||||
let mut params: HashMap<Rc<str>, Rc<str>> = HashMap::new();
|
||||
params.insert("idx".into(), idx.to_string().into());
|
||||
params.insert("src".into(), "".to_string().into());
|
||||
params.insert("src".into(), String::new().into());
|
||||
parser_state.instantiate_template(
|
||||
doc_params, "Device", layout, widget, params,
|
||||
)?;
|
||||
@@ -309,7 +309,7 @@ pub fn create_watch(app: &mut AppState) -> anyhow::Result<OverlayWindowConfig> {
|
||||
} else {
|
||||
com.alterables
|
||||
.set_style(*div, StyleSetRequest::Display(taffy::Display::None));
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user