system settings
git clone https://git.lucas.co/cce-system-interface.git
Refactor Processors settings page to Hardware and optimize typeface layout
src/app.rs | 12 +-
src/main.rs | 435 +++++++++++++++++++++-----
src/pages/{processors.rs => hardware.rs} | 22 +-
src/pages/mod.rs | 8 +-
src/pages/services.rs | 230 +-------------
src/pages/typeface.rs | 519 +++++++++++++++----------------
6 files changed, 643 insertions(+), 583 deletions(-)
diff --git a/src/app.rs b/src/app.rs
index c3e0548..0647030 100644
--- a/src/app.rs
+++ b/src/app.rs
@@ -7,7 +7,7 @@ use crate::pages::layout;
use crate::pages::network;
use crate::pages::notifications;
use crate::pages::power;
-use crate::pages::processors;
+use crate::pages::hardware;
use crate::pages::status;
use crate::pages::storage;
use crate::pages::system_info;
@@ -25,7 +25,7 @@ pub struct AppState {
pub network: network::NetworkState,
pub layout: layout::LayoutState,
pub input: input::InputState,
- pub processors: processors::ProcessorsState,
+ pub hardware: hardware::HardwareState,
pub system_info: system_info::SystemState,
pub status: status::StatusState,
pub storage: storage::StorageState,
@@ -46,7 +46,7 @@ impl Default for AppState {
network: network::NetworkState::default(),
layout: layout::LayoutState::default(),
input: input::InputState::default(),
- processors: processors::ProcessorsState::default(),
+ hardware: hardware::HardwareState::default(),
system_info: system_info::SystemState::default(),
status: status::StatusState::default(),
storage: storage::StorageState::default(),
@@ -67,7 +67,7 @@ pub enum AppAction {
Radios(network::NetworkMessage),
Layout(layout::LayoutMessage),
Input(input::InputMessage),
- Processors(processors::ProcessorsMessage),
+ Hardware(hardware::HardwareMessage),
SystemInfo(system_info::SystemMessage),
Status(status::StatusMessage),
Storage(storage::StorageMessage),
@@ -144,4 +144,8 @@ impl RenderTarget for PageContent {
fn text(&mut self, content: &str, x: f32, y: f32, size: f32, color: [f32; 4]) {
self.texts.push((content.to_string(), size, x, y, color, None));
}
+
+ fn text_with_font(&mut self, content: &str, x: f32, y: f32, size: f32, color: [f32; 4], font: &str) {
+ self.texts.push((content.to_string(), size, x, y, color, Some(font.to_string())));
+ }
}
diff --git a/src/main.rs b/src/main.rs
index 3cb4510..c457c19 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -83,12 +83,72 @@ fn make_text_buffer(fs: &mut FontSystem, text: &str, size: f32) -> Buffer {
buf
}
-fn make_text_buffer_with_font(fs: &mut FontSystem, text: &str, size: f32, font: Option<&str>) -> Buffer {
+fn find_cased_family(fs: &FontSystem, name: &str) -> Option<String> {
+ let lower_name = name.to_lowercase();
+ for face in fs.db().faces() {
+ for (family, _) in &face.families {
+ if family.to_lowercase() == lower_name {
+ return Some(family.clone());
+ }
+ }
+ }
+ None
+}
+
+fn make_text_buffer_with_font(
+ fs: &mut FontSystem,
+ text: &str,
+ size: f32,
+ font: Option<&str>,
+ sans_fallback: &str,
+ serif_fallback: &str,
+ mono_fallback: &str,
+) -> Buffer {
let metrics = Metrics::new(size, size * 1.4);
let mut buf = Buffer::new(fs, metrics);
let mut attrs = Attrs::new();
+ let mut resolved_storage = None;
if let Some(font_name) = font {
- attrs = attrs.family(glyphon::Family::Name(font_name));
+ let family = match font_name {
+ "monospace" => {
+ if !mono_fallback.is_empty() {
+ resolved_storage = find_cased_family(fs, mono_fallback);
+ if let Some(ref cased) = resolved_storage {
+ glyphon::Family::Name(cased)
+ } else {
+ glyphon::Family::Name(mono_fallback)
+ }
+ } else {
+ glyphon::Family::Monospace
+ }
+ }
+ "sans-serif" => {
+ if !sans_fallback.is_empty() {
+ resolved_storage = find_cased_family(fs, sans_fallback);
+ if let Some(ref cased) = resolved_storage {
+ glyphon::Family::Name(cased)
+ } else {
+ glyphon::Family::Name(sans_fallback)
+ }
+ } else {
+ glyphon::Family::SansSerif
+ }
+ }
+ "serif" => {
+ if !serif_fallback.is_empty() {
+ resolved_storage = find_cased_family(fs, serif_fallback);
+ if let Some(ref cased) = resolved_storage {
+ glyphon::Family::Name(cased)
+ } else {
+ glyphon::Family::Name(serif_fallback)
+ }
+ } else {
+ glyphon::Family::Serif
+ }
+ }
+ _ => glyphon::Family::Name(font_name),
+ };
+ attrs = attrs.family(family);
}
buf.set_text(fs, text, attrs, glyphon::Shaping::Advanced);
buf.shape_until_scroll(fs, true);
@@ -158,7 +218,7 @@ struct SystemInterface {
rx_layout: std::sync::mpsc::Receiver<pages::layout::LayoutState>,
rx_input: std::sync::mpsc::Receiver<pages::input::InputState>,
rx_fingers: std::sync::mpsc::Receiver<Vec<pages::input::Finger>>,
- rx_processors: std::sync::mpsc::Receiver<pages::processors::ProcessorsState>,
+ rx_hardware: std::sync::mpsc::Receiver<pages::hardware::HardwareState>,
rx_system: std::sync::mpsc::Receiver<pages::system_info::SystemState>,
rx_status: std::sync::mpsc::Receiver<pages::status::StatusState>,
rx_storage: std::sync::mpsc::Receiver<pages::storage::StorageState>,
@@ -180,6 +240,9 @@ struct SystemInterface {
max_scroll_y: f32,
page_root_container: clear_ui::widget::Container,
page_sec_containers: Vec<clear_ui::widget::Container>,
+ sans_serif_family: String,
+ serif_family: String,
+ monospace_family: String,
}
impl SystemInterface {
@@ -355,7 +418,7 @@ impl SystemInterface {
rx
};
let rx_system = spawn_bg(5, || pages::system_info::fetch_system_state());
- let rx_processors = spawn_bg(3, || pages::processors::fetch_processors_state());
+ let rx_hardware = spawn_bg(3, || pages::hardware::fetch_hardware_state());
let rx_status = spawn_bg(10, || pages::status::fetch_status_state());
let rx_storage = spawn_bg(10, || pages::storage::fetch_storage_state());
let rx_notifications = {
@@ -388,6 +451,8 @@ impl SystemInterface {
let (tx_color_selector, rx_color_selector) = std::sync::mpsc::channel();
let scale_factor = scale;
+ let (sans_family, serif_family, monospace_family, _, _, _, _) = pages::typeface::read_preferred_fonts();
+
let mut this = Self {
window, surface, wgpu_surface, device, queue, config, render_pipeline,
vertex_buffer, vertex_count: 0,
@@ -398,7 +463,7 @@ impl SystemInterface {
cursor_x: 0.0, cursor_y: 0.0,
scale_factor,
rx_power, rx_audio, rx_display, rx_network, rx_layout, rx_input, rx_fingers,
- rx_processors, rx_system, rx_status, rx_storage, rx_notifications,
+ rx_hardware, rx_system, rx_status, rx_storage, rx_notifications,
rx_backup_state, rx_typeface, rx_services, rx_colors, tx_backup, rx_backup,
tx_color_selector, rx_color_selector,
width, height,
@@ -407,6 +472,9 @@ impl SystemInterface {
max_scroll_y: 0.0,
page_root_container: clear_ui::widget::Container::new(),
page_sec_containers: Vec::new(),
+ sans_serif_family: sans_family,
+ serif_family,
+ monospace_family,
};
this.rebuild_layout(width as f32, height as f32);
this
@@ -498,7 +566,15 @@ impl SystemInterface {
}
for (t, size, x, y, tc, font_opt) in &pc.texts {
text_items.push(TextItem {
- buffer: make_text_buffer_with_font(&mut self.font_system, t, *size * s, font_opt.as_deref()),
+ buffer: make_text_buffer_with_font(
+ &mut self.font_system,
+ t,
+ *size * s,
+ font_opt.as_deref(),
+ &self.sans_serif_family,
+ &self.serif_family,
+ &self.monospace_family,
+ ),
x: *x * s, y: (*y - scroll_offset_y) * s,
color: glyphon::Color::rgb(
(tc[0] * 255.0) as u8, (tc[1] * 255.0) as u8, (tc[2] * 255.0) as u8,
@@ -526,8 +602,8 @@ impl SystemInterface {
&& btn.y >= sb_y - 1.0 && btn.y + btn.h <= sb_y + sb_h + 1.0 {
left_align = true;
}
- } else if self.app.current_page == Page::Processors {
- let sb = &self.app.processors.cpu_list_box;
+ } else if self.app.current_page == Page::Hardware {
+ let sb = &self.app.hardware.cpu_list_box;
let (sb_x, sb_y, sb_w, sb_h) = sb.rect();
if btn.x >= sb_x - 1.0 && btn.x + btn.w <= sb_x + sb_w + 1.0
&& btn.y >= sb_y - 1.0 && btn.y + btn.h <= sb_y + sb_h + 1.0 {
@@ -587,7 +663,7 @@ impl SystemInterface {
self.app.services.list_box.scroll_box.clear_children(); self.app.services.list_box.scroll_box.set_parent(None);
- self.app.processors.cpu_list_box.scroll_box.clear_children(); self.app.processors.cpu_list_box.scroll_box.set_parent(None);
+ self.app.hardware.cpu_list_box.scroll_box.clear_children(); self.app.hardware.cpu_list_box.scroll_box.set_parent(None);
self.app.network.wifi_list_box.scroll_box.clear_children(); self.app.network.wifi_list_box.scroll_box.set_parent(None);
@@ -652,8 +728,8 @@ impl SystemInterface {
link_parent_child(&mut self.page_root_container, &mut self.app.services.search_box);
link_parent_child(&mut self.page_root_container, &mut self.app.services.list_box.scroll_box);
}
- Page::Processors => {
- link_parent_child(&mut self.page_root_container, &mut self.app.processors.cpu_list_box.scroll_box);
+ Page::Hardware => {
+ link_parent_child(&mut self.page_root_container, &mut self.app.hardware.cpu_list_box.scroll_box);
}
Page::Radios => {
link_parent_child(&mut self.page_root_container, &mut self.app.network.wifi_list_box.scroll_box);
@@ -726,6 +802,52 @@ impl SystemInterface {
_ => {}
}
+ // Render context menu overlay if visible
+ if clear_ui::widget::context_menu::is_visible() {
+ let cx = clear_ui::widget::context_menu::x();
+ let cy = clear_ui::widget::context_menu::y();
+ let cw = clear_ui::widget::context_menu::w();
+ let ch = clear_ui::widget::context_menu::h();
+
+ // Border
+ widgets.push(AppWidget {
+ x: cx * s, y: cy * s, w: cw * s, h: ch * s,
+ color: [0.22, 0.22, 0.28, 1.0], hover_color: [0.22, 0.22, 0.28, 1.0],
+ hovering: false, kind: WidgetKind::Static,
+ });
+ // Bg
+ widgets.push(AppWidget {
+ x: (cx + 1.0) * s, y: (cy + 1.0) * s, w: (cw - 2.0) * s, h: (ch - 2.0) * s,
+ color: [0.06, 0.06, 0.09, 1.0], hover_color: [0.06, 0.06, 0.09, 1.0],
+ hovering: false, kind: WidgetKind::Static,
+ });
+
+ // Hover highlight
+ if let Some(h_idx) = clear_ui::widget::context_menu::hovered_item() {
+ let iy = cy + h_idx as f32 * 24.0;
+ widgets.push(AppWidget {
+ x: (cx + 2.0) * s, y: (iy + 2.0) * s, w: (cw - 4.0) * s, h: 20.0 * s,
+ color: [0.20, 0.40, 0.65, 0.6], hover_color: [0.20, 0.40, 0.65, 0.6],
+ hovering: false, kind: WidgetKind::Static,
+ });
+ }
+
+ // Texts
+ for (idx, opt) in clear_ui::widget::context_menu::options().iter().enumerate() {
+ let iy = cy + idx as f32 * 24.0 + (24.0 - 12.0) / 2.0;
+ let text_color = if clear_ui::widget::context_menu::hovered_item() == Some(idx) {
+ glyphon::Color::rgb(0xff, 0xff, 0xff)
+ } else {
+ glyphon::Color::rgb(0xcc, 0xcc, 0xd4)
+ };
+ text_items.push(TextItem {
+ buffer: make_text_buffer(&mut self.font_system, opt, 12.0 * s),
+ x: (cx + 8.0) * s, y: iy * s,
+ color: text_color,
+ });
+ }
+ }
+
self.widgets = widgets;
self.text_items = text_items;
self.page_buttons = page_buttons;
@@ -744,7 +866,7 @@ impl SystemInterface {
Page::Display => display::view(&mut self.app.display, cx, cy, cw, ch),
Page::Radios => network::view(&mut self.app.network, cx, cy, cw, ch, root_focused),
Page::Layout => layout::view(&mut self.app.layout, cx, cy, cw, ch, &sec_focused),
- Page::Processors => processors::view(&mut self.app.processors, cx, cy, cw, ch, root_focused),
+ Page::Hardware => hardware::view(&mut self.app.hardware, cx, cy, cw, ch, root_focused),
Page::Input => input::view(&mut self.app.input, cx, cy, cw, ch, &sec_focused),
Page::System => system_info::view(&self.app.system_info, cx, cy, cw, ch),
Page::Status => status::view(&mut self.app.status, cx, cy, cw, ch),
@@ -852,8 +974,8 @@ impl SystemInterface {
system_info::update(&mut self.app.system_info, system_info::SystemMessage::Refreshed(s));
self.needs_rebuild = true;
}
- while let Ok(s) = self.rx_processors.try_recv() {
- processors::update(&mut self.app.processors, processors::ProcessorsMessage::Refreshed(s));
+ while let Ok(s) = self.rx_hardware.try_recv() {
+ hardware::update(&mut self.app.hardware, hardware::HardwareMessage::Refreshed(s));
self.needs_rebuild = true;
}
while let Ok(s) = self.rx_status.try_recv() {
@@ -873,6 +995,9 @@ impl SystemInterface {
self.needs_rebuild = true;
}
while let Ok(s) = self.rx_typeface.try_recv() {
+ self.sans_serif_family = s.sans_serif.clone();
+ self.serif_family = s.serif.clone();
+ self.monospace_family = s.monospace.clone();
typeface::update(&mut self.app.typeface, typeface::TypefaceMessage::Refreshed(s));
self.needs_rebuild = true;
}
@@ -911,11 +1036,16 @@ impl SystemInterface {
AppAction::Layout(m) => layout::update(&mut self.app.layout, m.clone()),
AppAction::Input(m) => input::update(&mut self.app.input, m.clone()),
AppAction::SystemInfo(m) => system_info::update(&mut self.app.system_info, m.clone()),
- AppAction::Processors(m) => processors::update(&mut self.app.processors, m.clone()),
+ AppAction::Hardware(m) => hardware::update(&mut self.app.hardware, m.clone()),
AppAction::Status(m) => status::update(&mut self.app.status, m.clone()),
AppAction::Storage(m) => storage::update(&mut self.app.storage, m.clone()),
AppAction::Notifications(m) => notifications::update(&mut self.app.notifications, m.clone()),
- AppAction::Typeface(m) => typeface::update(&mut self.app.typeface, m.clone()),
+ AppAction::Typeface(m) => {
+ typeface::update(&mut self.app.typeface, m.clone());
+ self.sans_serif_family = self.app.typeface.sans_serif.clone();
+ self.serif_family = self.app.typeface.serif.clone();
+ self.monospace_family = self.app.typeface.monospace.clone();
+ }
AppAction::Services(m) => services::update(&mut self.app.services, m.clone()),
AppAction::Colors(m) => colors::update(&mut self.app.colors, m.clone()),
AppAction::Backup(m) => match m {
@@ -936,6 +1066,17 @@ impl SystemInterface {
self.cursor_x = x;
self.cursor_y = y;
let s = self.scale_factor as f32;
+
+ if clear_ui::widget::context_menu::is_visible() {
+ let lx_no_scroll = x / s;
+ let ly_no_scroll = y / s;
+ if clear_ui::widget::context_menu::cursor_moved(lx_no_scroll, ly_no_scroll) {
+ self.needs_rebuild = true;
+ return true;
+ }
+ return false;
+ }
+
let lx = self.cursor_x / s;
let ly = self.cursor_y / s + self.scroll_y;
let mut changed = false;
@@ -1044,16 +1185,16 @@ impl SystemInterface {
changed = true;
}
}
- if self.app.current_page == Page::Processors {
- if self.app.processors.cpu_label.cursor_moved(lx, ly) {
+ if self.app.current_page == Page::Hardware {
+ if self.app.hardware.cpu_label.cursor_moved(lx, ly) {
changed = true;
}
- for gpu_lbl in &mut self.app.processors.gpu_labels {
+ for gpu_lbl in &mut self.app.hardware.gpu_labels {
if gpu_lbl.cursor_moved(lx, ly) {
changed = true;
}
}
- if self.app.processors.cpu_list_box.cursor_moved(lx, ly) {
+ if self.app.hardware.cpu_list_box.cursor_moved(lx, ly) {
changed = true;
}
}
@@ -1105,6 +1246,18 @@ impl SystemInterface {
if self.app.typeface.list_box.cursor_moved(lx, ly) {
changed = true;
}
+ if self.app.typeface.borders_size_box.cursor_moved(lx, ly) {
+ changed = true;
+ }
+ if self.app.typeface.status_size_box.cursor_moved(lx, ly) {
+ changed = true;
+ }
+ if self.app.typeface.fuzzel_size_box.cursor_moved(lx, ly) {
+ changed = true;
+ }
+ if self.app.typeface.terminal_size_box.cursor_moved(lx, ly) {
+ changed = true;
+ }
}
if self.app.current_page == Page::Services {
if self.app.services.search_box.cursor_moved(lx, ly) {
@@ -1119,9 +1272,19 @@ impl SystemInterface {
}
fn handle_mouse_input(&mut self, button: clear_ui::widget::MouseButton, state: clear_ui::widget::ElementState) -> bool {
- if button != clear_ui::widget::MouseButton::Left { return false; }
let s = self.scale_factor as f32;
- if state == clear_ui::widget::ElementState::Released {
+ let lx_no_scroll = self.cursor_x / s;
+ let ly_no_scroll = self.cursor_y / s;
+
+ if clear_ui::widget::context_menu::is_visible() {
+ if clear_ui::widget::context_menu::mouse_input(button, state, lx_no_scroll, ly_no_scroll) {
+ self.needs_rebuild = true;
+ return true;
+ }
+ }
+
+ if button != clear_ui::widget::MouseButton::Left && button != clear_ui::widget::MouseButton::Right { return false; }
+ if button == clear_ui::widget::MouseButton::Left && state == clear_ui::widget::ElementState::Released {
let (px, py) = (self.cursor_x, self.cursor_y);
for btn in &self.page_buttons.clone() {
if px >= btn.x && px <= btn.x + btn.w && py >= btn.y && py <= btn.y + btn.h {
@@ -1218,9 +1381,9 @@ impl SystemInterface {
if srv.search_box.hit_test(lx, ly) { clicked_any_focusable = true; }
if srv.list_box.hit_test(lx, ly) { clicked_any_focusable = true; }
}
- Page::Processors => {
- let proc = &mut self.app.processors;
- if proc.cpu_list_box.hit_test(lx, ly) { clicked_any_focusable = true; }
+ Page::Hardware => {
+ let hw = &mut self.app.hardware;
+ if hw.cpu_list_box.hit_test(lx, ly) { clicked_any_focusable = true; }
}
Page::Radios => {
let net = &mut self.app.network;
@@ -1420,123 +1583,163 @@ impl SystemInterface {
if !lbl2.hit_test(lx, ly) { lbl2.unfocus(); }
lbl2.mouse_input(button, state, lx, ly);
}
- if state == clear_ui::widget::ElementState::Pressed && self.app.current_page == Page::Typefaces {
+ if self.app.current_page == Page::Typefaces {
let tb = &mut self.app.typeface.sans_box;
- if !tb.hit_test(lx, ly) { tb.unfocus(); }
+ if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
if tb.mouse_input(button, state, lx, ly) {
self.needs_rebuild = true;
}
- if tb.take_change() {
+ if state == clear_ui::widget::ElementState::Pressed && tb.take_change() {
actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetSans(tb.text.clone())));
}
let tb = &mut self.app.typeface.serif_box;
- if !tb.hit_test(lx, ly) { tb.unfocus(); }
+ if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
if tb.mouse_input(button, state, lx, ly) {
self.needs_rebuild = true;
}
- if tb.take_change() {
+ if state == clear_ui::widget::ElementState::Pressed && tb.take_change() {
actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetSerif(tb.text.clone())));
}
let tb = &mut self.app.typeface.mono_box;
- if !tb.hit_test(lx, ly) { tb.unfocus(); }
+ if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
if tb.mouse_input(button, state, lx, ly) {
self.needs_rebuild = true;
}
- if tb.take_change() {
+ if state == clear_ui::widget::ElementState::Pressed && tb.take_change() {
actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetMono(tb.text.clone())));
}
let menu = &mut self.app.typeface.borders_menu;
- if !menu.hit_test(lx, ly) { menu.unfocus(); }
+ if state == clear_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly) { menu.unfocus(); }
if menu.mouse_input(button, state, lx, ly) {
self.needs_rebuild = true;
}
- if menu.take_change() {
+ if state == clear_ui::widget::ElementState::Pressed && menu.take_change() {
actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetBordersMenu(menu.selected)));
}
let tb = &mut self.app.typeface.borders_box;
- if !tb.hit_test(lx, ly) { tb.unfocus(); }
+ if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
if tb.mouse_input(button, state, lx, ly) {
self.needs_rebuild = true;
}
- if tb.take_change() {
+ if state == clear_ui::widget::ElementState::Pressed && tb.take_change() {
actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetBorders(tb.text.clone())));
}
let menu = &mut self.app.typeface.status_menu;
- if !menu.hit_test(lx, ly) { menu.unfocus(); }
+ if state == clear_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly) { menu.unfocus(); }
if menu.mouse_input(button, state, lx, ly) {
self.needs_rebuild = true;
}
- if menu.take_change() {
+ if state == clear_ui::widget::ElementState::Pressed && menu.take_change() {
actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetStatusMenu(menu.selected)));
}
let tb = &mut self.app.typeface.status_box;
- if !tb.hit_test(lx, ly) { tb.unfocus(); }
+ if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
if tb.mouse_input(button, state, lx, ly) {
self.needs_rebuild = true;
}
- if tb.take_change() {
+ if state == clear_ui::widget::ElementState::Pressed && tb.take_change() {
actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetStatus(tb.text.clone())));
}
let menu = &mut self.app.typeface.fuzzel_menu;
- if !menu.hit_test(lx, ly) { menu.unfocus(); }
+ if state == clear_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly) { menu.unfocus(); }
if menu.mouse_input(button, state, lx, ly) {
self.needs_rebuild = true;
}
- if menu.take_change() {
+ if state == clear_ui::widget::ElementState::Pressed && menu.take_change() {
actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetFuzzelMenu(menu.selected)));
}
let tb = &mut self.app.typeface.fuzzel_box;
- if !tb.hit_test(lx, ly) { tb.unfocus(); }
+ if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
if tb.mouse_input(button, state, lx, ly) {
self.needs_rebuild = true;
}
- if tb.take_change() {
+ if state == clear_ui::widget::ElementState::Pressed && tb.take_change() {
actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetFuzzel(tb.text.clone())));
}
let menu = &mut self.app.typeface.terminal_menu;
- if !menu.hit_test(lx, ly) { menu.unfocus(); }
+ if state == clear_ui::widget::ElementState::Pressed && !menu.hit_test(lx, ly) { menu.unfocus(); }
if menu.mouse_input(button, state, lx, ly) {
self.needs_rebuild = true;
}
- if menu.take_change() {
+ if state == clear_ui::widget::ElementState::Pressed && menu.take_change() {
actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetTerminalMenu(menu.selected)));
}
let tb = &mut self.app.typeface.terminal_box;
- if !tb.hit_test(lx, ly) { tb.unfocus(); }
+ if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
if tb.mouse_input(button, state, lx, ly) {
self.needs_rebuild = true;
}
- if tb.take_change() {
+ if state == clear_ui::widget::ElementState::Pressed && tb.take_change() {
actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetTerminal(tb.text.clone())));
}
let tb = &mut self.app.typeface.search_box;
- if !tb.hit_test(lx, ly) { tb.unfocus(); }
+ if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
if tb.mouse_input(button, state, lx, ly) {
self.needs_rebuild = true;
}
- if tb.take_change() {
+ if state == clear_ui::widget::ElementState::Pressed && tb.take_change() {
actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetSearch(tb.text.clone())));
}
+ let sb = &mut self.app.typeface.borders_size_box;
+ if state == clear_ui::widget::ElementState::Pressed && !sb.hit_test(lx, ly) { sb.unfocus(); }
+ let old_val = sb.value;
+ if sb.mouse_input(button, state, lx, ly) {
+ self.needs_rebuild = true;
+ }
+ if sb.value != old_val {
+ actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetBordersSize(sb.value)));
+ }
+
+ let sb = &mut self.app.typeface.status_size_box;
+ if state == clear_ui::widget::ElementState::Pressed && !sb.hit_test(lx, ly) { sb.unfocus(); }
+ let old_val = sb.value;
+ if sb.mouse_input(button, state, lx, ly) {
+ self.needs_rebuild = true;
+ }
+ if sb.value != old_val {
+ actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetStatusSize(sb.value)));
+ }
+
+ let sb = &mut self.app.typeface.fuzzel_size_box;
+ if state == clear_ui::widget::ElementState::Pressed && !sb.hit_test(lx, ly) { sb.unfocus(); }
+ let old_val = sb.value;
+ if sb.mouse_input(button, state, lx, ly) {
+ self.needs_rebuild = true;
+ }
+ if sb.value != old_val {
+ actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetFuzzelSize(sb.value)));
+ }
+
+ let sb = &mut self.app.typeface.terminal_size_box;
+ if state == clear_ui::widget::ElementState::Pressed && !sb.hit_test(lx, ly) { sb.unfocus(); }
+ let old_val = sb.value;
+ if sb.mouse_input(button, state, lx, ly) {
+ self.needs_rebuild = true;
+ }
+ if sb.value != old_val {
+ actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetTerminalSize(sb.value)));
+ }
+
let tf = &mut self.app.typeface;
if tf.list_box.mouse_input(button, state, lx, ly) {
self.needs_rebuild = true;
}
}
- if state == clear_ui::widget::ElementState::Pressed && self.app.current_page == Page::Services {
+ if self.app.current_page == Page::Services {
let tb = &mut self.app.services.search_box;
- if !tb.hit_test(lx, ly) { tb.unfocus(); }
+ if state == clear_ui::widget::ElementState::Pressed && !tb.hit_test(lx, ly) { tb.unfocus(); }
if tb.mouse_input(button, state, lx, ly) {
self.needs_rebuild = true;
}
@@ -1545,9 +1748,9 @@ impl SystemInterface {
self.needs_rebuild = true;
}
}
- if state == clear_ui::widget::ElementState::Pressed && self.app.current_page == Page::Processors {
- let proc = &mut self.app.processors;
- if proc.cpu_list_box.mouse_input(button, state, lx, ly) {
+ if state == clear_ui::widget::ElementState::Pressed && self.app.current_page == Page::Hardware {
+ let hw = &mut self.app.hardware;
+ if hw.cpu_list_box.mouse_input(button, state, lx, ly) {
self.needs_rebuild = true;
}
}
@@ -1588,9 +1791,9 @@ impl SystemInterface {
return true;
}
}
- if self.app.current_page == Page::Processors {
- let proc = &mut self.app.processors;
- if proc.cpu_list_box.mouse_wheel(delta, lx, ly) {
+ if self.app.current_page == Page::Hardware {
+ let hw = &mut self.app.hardware;
+ if hw.cpu_list_box.mouse_wheel(delta, lx, ly) {
self.needs_rebuild = true;
return true;
}
@@ -1620,7 +1823,7 @@ impl SystemInterface {
fn get_page_root_widget(&mut self) -> Option<*mut (dyn clear_ui::widget::Widget + 'static)> {
match self.app.current_page {
- Page::Typefaces | Page::Services | Page::Processors | Page::Radios |
+ Page::Typefaces | Page::Services | Page::Hardware | Page::Radios |
Page::Layout | Page::Colors | Page::Notifications | Page::Input |
Page::Audio | Page::Display => {
let ptr = &mut self.page_root_container as &mut dyn clear_ui::widget::Widget as *mut dyn clear_ui::widget::Widget;
@@ -1831,10 +2034,7 @@ impl SystemInterface {
(clear_ui::widget::Key::Named(clear_ui::widget::NamedKey::ArrowUp), false) => true,
_ => false,
};
- if is_down {
- if !clear_ui::widget::focus::is_focused(&self.app.typeface.list_box.scroll_box) {
- return false;
- }
+ if is_down && clear_ui::widget::focus::is_focused(&self.app.typeface.list_box.scroll_box) {
let next_idx_font_scroll = {
let tf = &self.app.typeface;
let query = tf.search_box.text.to_lowercase();
@@ -1875,10 +2075,7 @@ impl SystemInterface {
self.needs_rebuild = true;
return true;
}
- } else if is_up {
- if !clear_ui::widget::focus::is_focused(&self.app.typeface.list_box.scroll_box) {
- return false;
- }
+ } else if is_up && clear_ui::widget::focus::is_focused(&self.app.typeface.list_box.scroll_box) {
let next_idx_font_scroll = {
let tf = &self.app.typeface;
let query = tf.search_box.text.to_lowercase();
@@ -1989,6 +2186,30 @@ impl SystemInterface {
}
consumed = true;
}
+
+ let sb = &mut self.app.typeface.borders_size_box;
+ if sb.keyboard_input(event) {
+ actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetBordersSize(sb.value)));
+ consumed = true;
+ }
+
+ let sb = &mut self.app.typeface.status_size_box;
+ if sb.keyboard_input(event) {
+ actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetStatusSize(sb.value)));
+ consumed = true;
+ }
+
+ let sb = &mut self.app.typeface.fuzzel_size_box;
+ if sb.keyboard_input(event) {
+ actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetFuzzelSize(sb.value)));
+ consumed = true;
+ }
+
+ let sb = &mut self.app.typeface.terminal_size_box;
+ if sb.keyboard_input(event) {
+ actions.push(AppAction::Typeface(pages::typeface::TypefaceMessage::SetTerminalSize(sb.value)));
+ consumed = true;
+ }
for a in &actions {
self.handle_action(a);
@@ -2011,9 +2232,9 @@ impl SystemInterface {
return true;
}
}
- if self.app.current_page == Page::Processors {
- let proc = &mut self.app.processors;
- if proc.cpu_list_box.keyboard_input(event) {
+ if self.app.current_page == Page::Hardware {
+ let hw = &mut self.app.hardware;
+ if hw.cpu_list_box.keyboard_input(event) {
self.needs_rebuild = true;
return true;
}
@@ -2084,6 +2305,29 @@ impl SystemInterface {
}
}
+struct PressedKey {
+ logical_key: clear_ui::widget::Key,
+ text: Option<String>,
+ first_pressed: std::time::Instant,
+ last_repeated: std::time::Instant,
+}
+
+fn is_repeatable_key(key: &clear_ui::widget::Key) -> bool {
+ use clear_ui::widget::{Key, NamedKey};
+ match key {
+ Key::Named(NamedKey::Backspace) |
+ Key::Named(NamedKey::Delete) |
+ Key::Named(NamedKey::ArrowLeft) |
+ Key::Named(NamedKey::ArrowRight) |
+ Key::Named(NamedKey::ArrowUp) |
+ Key::Named(NamedKey::ArrowDown) |
+ Key::Named(NamedKey::Home) |
+ Key::Named(NamedKey::End) |
+ Key::Character(_) => true,
+ _ => false,
+ }
+}
+
struct App {
registry_state: RegistryState,
compositor_state: CompositorState,
@@ -2104,6 +2348,8 @@ struct App {
exit: bool,
redraw: bool,
ctrl_pressed: bool,
+ shift_pressed: bool,
+ pressed_key: Option<PressedKey>,
}
@@ -2342,6 +2588,7 @@ impl KeyboardHandler for App {
_layout: u32,
) {
self.ctrl_pressed = modifiers.ctrl;
+ self.shift_pressed = modifiers.shift;
}
}
@@ -2380,8 +2627,28 @@ impl App {
text: event.utf8.clone(),
repeat: false,
ctrl: self.ctrl_pressed,
+ shift: self.shift_pressed,
};
+ if state == clear_ui::widget::ElementState::Pressed {
+ if is_repeatable_key(&custom_event.logical_key) {
+ self.pressed_key = Some(PressedKey {
+ logical_key: custom_event.logical_key.clone(),
+ text: custom_event.text.clone(),
+ first_pressed: std::time::Instant::now(),
+ last_repeated: std::time::Instant::now(),
+ });
+ } else {
+ self.pressed_key = None;
+ }
+ } else if state == clear_ui::widget::ElementState::Released {
+ if let Some(ref pk) = self.pressed_key {
+ if pk.logical_key == custom_event.logical_key {
+ self.pressed_key = None;
+ }
+ }
+ }
+
if let Some(st) = &mut self.state {
if st.handle_key_input(&custom_event) {
self.redraw = true;
@@ -2493,6 +2760,8 @@ fn main() {
exit: false,
redraw: true,
ctrl_pressed: false,
+ shift_pressed: false,
+ pressed_key: None,
};
// Perform a roundtrip to populate output_state with active output scales
@@ -2520,6 +2789,9 @@ fn main() {
let loop_handle = event_loop.handle();
WaylandSource::new(conn, event_queue).insert(loop_handle.clone()).unwrap();
+ const KEY_REPEAT_DELAY: std::time::Duration = std::time::Duration::from_millis(500);
+ const KEY_REPEAT_INTERVAL: std::time::Duration = std::time::Duration::from_millis(50);
+
loop {
event_loop
.dispatch(std::time::Duration::from_millis(16), &mut app)
@@ -2533,6 +2805,29 @@ fn main() {
app.redraw = true;
}
}
+
+ if let Some(ref mut pk) = app.pressed_key {
+ let now = std::time::Instant::now();
+ if now.duration_since(pk.first_pressed) >= KEY_REPEAT_DELAY {
+ if now.duration_since(pk.last_repeated) >= KEY_REPEAT_INTERVAL {
+ pk.last_repeated = now;
+ let custom_event = clear_ui::widget::KeyEvent {
+ state: clear_ui::widget::ElementState::Pressed,
+ logical_key: pk.logical_key.clone(),
+ text: pk.text.clone(),
+ repeat: true,
+ ctrl: app.ctrl_pressed,
+ shift: app.shift_pressed,
+ };
+ if let Some(st) = &mut app.state {
+ if st.handle_key_input(&custom_event) {
+ app.redraw = true;
+ }
+ }
+ }
+ }
+ }
+
if app.redraw {
app.redraw = false;
if let Some(state) = &mut app.state {
diff --git a/src/pages/processors.rs b/src/pages/hardware.rs
similarity index 95%
rename from src/pages/processors.rs
rename to src/pages/hardware.rs
index a225900..7b0d1f4 100644
--- a/src/pages/processors.rs
+++ b/src/pages/hardware.rs
@@ -3,7 +3,7 @@ use clear_ui::layout::Section;
use clear_ui::widget::{Label, ScrollingList};
#[derive(Debug, Clone)]
-pub struct ProcessorsState {
+pub struct HardwareState {
pub cpu_model: String,
pub cpu_usage: f32,
pub cpu_cores: u32,
@@ -15,7 +15,7 @@ pub struct ProcessorsState {
pub cpu_list_box: ScrollingList,
}
-impl Default for ProcessorsState {
+impl Default for HardwareState {
fn default() -> Self {
Self {
cpu_model: String::new(),
@@ -32,8 +32,8 @@ impl Default for ProcessorsState {
}
#[derive(Debug, Clone)]
-pub enum ProcessorsMessage {
- Refreshed(ProcessorsState),
+pub enum HardwareMessage {
+ Refreshed(HardwareState),
None,
}
@@ -95,7 +95,7 @@ async fn read_nvidia_gpu_temp() -> Option<f32> {
val_str.trim().parse::<f32>().ok()
}
-pub async fn fetch_processors_state() -> ProcessorsState {
+pub async fn fetch_hardware_state() -> HardwareState {
let (cpu_model, cpu_cores) = {
let lscpu = tokio::process::Command::new("lscpu")
.output().await.ok()
@@ -188,7 +188,7 @@ pub async fn fetch_processors_state() -> ProcessorsState {
Label::new(&text).with_font_size(12.0).with_color([212, 212, 212])
}).collect();
- ProcessorsState {
+ HardwareState {
cpu_model,
cpu_usage,
cpu_cores,
@@ -204,7 +204,7 @@ pub async fn fetch_processors_state() -> ProcessorsState {
const TEXT_FG: [f32; 4] = [0.83, 0.83, 0.83, 1.0];
const TEXT_DIM: [f32; 4] = [0.53, 0.53, 0.60, 1.0];
-pub fn view(state: &mut ProcessorsState, cx: f32, cy: f32, cw: f32, _ch: f32, root_focused: bool) -> PageContent {
+pub fn view(state: &mut HardwareState, cx: f32, cy: f32, cw: f32, _ch: f32, root_focused: bool) -> PageContent {
let mut pc = PageContent::new();
let mut y = cy + 12.0;
@@ -255,7 +255,7 @@ pub fn view(state: &mut ProcessorsState, cx: f32, cy: f32, cw: f32, _ch: f32, ro
[0.0, 0.0, 0.0, 0.0],
[1.0, 1.0, 1.0, 0.06],
[0.0, 0.0, 0.0, 0.0],
- crate::app::AppAction::Processors(ProcessorsMessage::None),
+ crate::app::AppAction::Hardware(HardwareMessage::None),
);
pc.text(pid, list_box_x + 12.0, draw_y + 6.0, 12.0, [0.80, 0.80, 0.85, 1.0]);
@@ -288,9 +288,9 @@ pub fn view(state: &mut ProcessorsState, cx: f32, cy: f32, cw: f32, _ch: f32, ro
pc
}
-pub fn update(state: &mut ProcessorsState, msg: ProcessorsMessage) {
+pub fn update(state: &mut HardwareState, msg: HardwareMessage) {
match msg {
- ProcessorsMessage::Refreshed(new) => {
+ HardwareMessage::Refreshed(new) => {
state.loaded = new.loaded;
state.cpu_model = new.cpu_model;
state.cpu_usage = new.cpu_usage;
@@ -303,6 +303,6 @@ pub fn update(state: &mut ProcessorsState, msg: ProcessorsMessage) {
state.cpu_list_box = new.cpu_list_box;
state.cpu_list_box.set_scroll_y(old_scroll);
}
- ProcessorsMessage::None => {}
+ HardwareMessage::None => {}
}
}
diff --git a/src/pages/mod.rs b/src/pages/mod.rs
index 56878b4..ce677a1 100644
--- a/src/pages/mod.rs
+++ b/src/pages/mod.rs
@@ -8,7 +8,7 @@ pub mod system_info;
pub mod keybindings;
pub mod input;
pub mod status;
-pub mod processors;
+pub mod hardware;
pub mod notifications;
pub mod backup;
pub mod typeface;
@@ -25,7 +25,7 @@ pub enum Page {
Display,
Layout,
System,
- Processors,
+ Hardware,
Input,
Status,
Notifications,
@@ -43,7 +43,7 @@ impl Page {
Page::Input,
Page::Layout,
Page::Notifications,
- Page::Processors,
+ Page::Hardware,
Page::Power,
Page::Radios,
Page::Services,
@@ -63,7 +63,7 @@ impl Page {
Page::Display => "Display",
Page::Layout => "Layout",
Page::System => "System",
- Page::Processors => "Processors",
+ Page::Hardware => "Hardware",
Page::Input => "Input",
Page::Status => "Status",
Page::Notifications => "Notifications",
diff --git a/src/pages/services.rs b/src/pages/services.rs
index e7fdb0a..47bdcb6 100644
--- a/src/pages/services.rs
+++ b/src/pages/services.rs
@@ -1,236 +1,8 @@
use crate::app::PageContent;
use clear_ui::layout::Section;
-use clear_ui::widget::{Widget, TextLabel, ScrollBox, ScrollingList};
+use clear_ui::widget::{Widget, TextLabel, ScrollBox, ScrollingList, TextBox};
use clear_ui::widget::{ElementState, KeyEvent, MouseButton, Key, NamedKey};
-// ── TextBox Widget ──
-
-#[derive(Debug, Clone)]
-pub struct TextBox {
- x: f32, y: f32, w: f32, h: f32,
- pub text: String,
- pub editing: bool,
- pub edit_buffer: String,
- hovered: bool,
- just_changed: bool,
- label: Option<String>,
- row_x: f32,
- row_w: f32,
- pub parent: Option<*mut (dyn Widget + 'static)>,
- pub children: Vec<*mut (dyn Widget + 'static)>,
-}
-
-impl TextBox {
- pub fn new(text: String) -> Self {
- Self {
- x: 0.0, y: 0.0, w: 0.0, h: 0.0,
- text,
- editing: false,
- edit_buffer: String::new(),
- hovered: false,
- just_changed: false,
- label: None,
- row_x: 0.0,
- row_w: 0.0,
- parent: None,
- children: Vec::new(),
- }
- }
-
- pub fn with_label(mut self, label: &str) -> Self {
- self.label = Some(label.to_string());
- self
- }
-
- pub fn set_label(&mut self, label: &str) {
- self.label = Some(label.to_string());
- }
-
- pub fn take_change(&mut self) -> bool {
- let changed = self.just_changed;
- self.just_changed = false;
- changed
- }
-}
-
-impl Default for TextBox {
- fn default() -> Self {
- Self::new(String::new())
- }
-}
-
-impl Widget for TextBox {
- fn rect(&self) -> (f32, f32, f32, f32) { (self.x, self.y, self.w, self.h) }
- fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) { self.x = x; self.y = y; self.w = w; self.h = h; }
- fn set_row_rect(&mut self, x: f32, w: f32) { self.row_x = x; self.row_w = w; }
- fn set_hovered(&mut self, v: bool) { self.hovered = v; }
- fn hovered(&self) -> bool { self.hovered }
-
- fn color(&self) -> [f32; 4] {
- [0.10, 0.10, 0.16, 1.0]
- }
-
- fn hit_test(&self, px: f32, py: f32) -> bool {
- let (x, y, w, h) = self.rect();
- let hx = if self.row_w > 0.0 { self.row_x } else { x };
- let hw = if self.row_w > 0.0 { self.row_w } else { w };
- let (hy, hh) = if self.label.is_some() {
- (y - 18.0, h + 18.0)
- } else {
- (y, h)
- };
- px >= hx && px <= hx + hw && py >= hy && py <= hy + hh
- }
-
- fn top_room(&self) -> f32 { if self.label.is_some() { 18.0 } else { 0.0 } }
-
- fn cursor_moved(&mut self, px: f32, py: f32) -> bool {
- let was = self.hovered;
- self.hovered = self.hit_test(px, py);
- was != self.hovered
- }
-
- fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32) -> bool {
- if button != MouseButton::Left { return false; }
- if state != ElementState::Pressed { return false; }
- let (x, y, w, h) = self.rect();
- let (hy, hh) = if self.label.is_some() {
- (y - 18.0, h + 18.0)
- } else {
- (y, h)
- };
- if !(px >= x && px <= x + w && py >= hy && py <= hy + hh) {
- return false;
- }
- self.focus();
- true
- }
-
- fn focus(&mut self) {
- if !self.editing {
- self.editing = true;
- self.edit_buffer = self.text.clone();
- clear_ui::widget::focus::set_focused(self);
- }
- }
-
- fn unfocus(&mut self) {
- if self.editing {
- self.editing = false;
- if self.text != self.edit_buffer {
- self.text = self.edit_buffer.clone();
- self.just_changed = true;
- }
- }
- }
-
- fn keyboard_input(&mut self, event: &KeyEvent) -> bool {
- if !self.editing { return false; }
- if event.state != ElementState::Pressed { return false; }
- match &event.logical_key {
- Key::Named(NamedKey::Backspace) => {
- self.edit_buffer.pop();
- true
- }
- Key::Named(NamedKey::Enter) => {
- self.text = self.edit_buffer.clone();
- self.editing = false;
- self.just_changed = true;
- true
- }
- Key::Named(NamedKey::Escape) => {
- self.editing = false;
- true
- }
- _ => {
- if let Some(text) = &event.text {
- if !event.repeat {
- for ch in text.chars() {
- if ch.is_alphanumeric() || ch == ' ' || ch == '-' || ch == '_' || ch == '*' || ch == '.' || ch == '@' {
- self.edit_buffer.push(ch);
- }
- }
- }
- }
- true
- }
- }
- }
-
- fn hover_highlight(&self) -> Option<[f32; 4]> {
- None
- }
-
- fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
- let mut quads = Vec::new();
- if self.hovered {
- let (hy, hh) = if self.label.is_some() {
- (self.y - 18.0, self.h + 18.0)
- } else {
- (self.y, self.h)
- };
- let hx = if self.row_w > 0.0 { self.row_x } else { self.x };
- let hw = if self.row_w > 0.0 { self.row_w } else { self.w };
- quads.push((hx, hy, hw, hh, [1.0, 1.0, 1.0, 0.06]));
- }
- let bg_color = if self.editing {
- [0.12, 0.12, 0.18, 1.0]
- } else {
- [0.08, 0.08, 0.12, 1.0]
- };
- let border_color = if self.editing {
- [0.30, 0.50, 0.32, 1.0]
- } else if self.hovered {
- [0.25, 0.25, 0.35, 1.0]
- } else {
- [0.18, 0.18, 0.24, 1.0]
- };
- quads.push((self.x, self.y, self.w, self.h, border_color));
- quads.push((self.x + 1.0, self.y + 1.0, self.w - 2.0, self.h - 2.0, bg_color));
- quads
- }
-
- fn text_labels(&self) -> Vec<TextLabel> {
- let mut labels = Vec::new();
- if let Some(ref label) = self.label {
- labels.push(TextLabel {
- text: label.clone(),
- x: self.x + 4.0,
- y: self.y - 14.0,
- font_size: 12.0,
- color: [0x83, 0x83, 0x8a],
- });
- }
- let val_text = if self.editing {
- format!("{}|", self.edit_buffer)
- } else {
- self.text.clone()
- };
- labels.push(TextLabel {
- text: val_text,
- x: self.x + 8.0,
- y: self.y + (self.h - 12.0) / 2.0,
- font_size: 13.0,
- color: if self.editing { [0xee, 0xee, 0xf5] } else { [0xcc, 0xcc, 0xd4] },
- });
- labels
- }
-
- fn parent(&self) -> Option<*mut (dyn Widget + 'static)> { self.parent }
- fn set_parent(&mut self, parent: Option<*mut (dyn Widget + 'static)>) { self.parent = parent; }
- fn children(&self) -> Vec<*mut (dyn Widget + 'static)> { self.children.clone() }
- fn add_child(&mut self, child: *mut (dyn Widget + 'static)) { self.children.push(child); }
- fn clear_children(&mut self) { self.children.clear(); }
-}
-
-impl Drop for TextBox {
- fn drop(&mut self) {
- clear_ui::widget::focus::clear_if_matches(self);
- }
-}
-
-unsafe impl Send for TextBox {}
-unsafe impl Sync for TextBox {}
// ── Service Types and Page State ──
diff --git a/src/pages/typeface.rs b/src/pages/typeface.rs
index 9bdab1c..2364a99 100644
--- a/src/pages/typeface.rs
+++ b/src/pages/typeface.rs
@@ -1,255 +1,11 @@
use std::fs;
use crate::app::PageContent;
use clear_ui::layout::Section;
-use clear_ui::widget::{Widget, TextLabel, ScrollBox, ScrollingList, Dropdown};
+use clear_ui::widget::{Widget, TextLabel, ScrollBox, ScrollingList, Dropdown, TextBox, Spinbox};
use clear_ui::widget::{ElementState, KeyEvent, MouseButton, Key, NamedKey};
const FONTS_CONF_PATH: &str = "/home/lsgalante/.config/fontconfig/fonts.conf";
-// ── TextBox Widget ──
-
-#[derive(Debug, Clone)]
-pub struct TextBox {
- x: f32, y: f32, w: f32, h: f32,
- pub text: String,
- pub editing: bool,
- pub edit_buffer: String,
- hovered: bool,
- just_changed: bool,
- label: Option<String>,
- row_x: f32,
- row_w: f32,
- pub disabled: bool,
- pub parent: Option<*mut (dyn Widget + 'static)>,
- pub children: Vec<*mut (dyn Widget + 'static)>,
-}
-
-impl TextBox {
- pub fn new(text: String) -> Self {
- Self {
- x: 0.0, y: 0.0, w: 0.0, h: 0.0,
- text,
- editing: false,
- edit_buffer: String::new(),
- hovered: false,
- just_changed: false,
- label: None,
- row_x: 0.0,
- row_w: 0.0,
- disabled: false,
- parent: None,
- children: Vec::new(),
- }
- }
-
- pub fn with_label(mut self, label: &str) -> Self {
- self.label = Some(label.to_string());
- self
- }
-
- pub fn set_label(&mut self, label: &str) {
- self.label = Some(label.to_string());
- }
-
- pub fn take_change(&mut self) -> bool {
- let changed = self.just_changed;
- self.just_changed = false;
- changed
- }
-}
-
-impl Default for TextBox {
- fn default() -> Self {
- Self::new(String::new())
- }
-}
-
-impl Widget for TextBox {
- fn rect(&self) -> (f32, f32, f32, f32) { (self.x, self.y, self.w, self.h) }
- fn set_rect(&mut self, x: f32, y: f32, w: f32, h: f32) { self.x = x; self.y = y; self.w = w; self.h = h; }
- fn set_row_rect(&mut self, x: f32, w: f32) { self.row_x = x; self.row_w = w; }
- fn set_hovered(&mut self, v: bool) { self.hovered = v; }
- fn hovered(&self) -> bool { self.hovered }
-
- fn color(&self) -> [f32; 4] {
- [0.10, 0.10, 0.16, 1.0]
- }
-
- fn hit_test(&self, px: f32, py: f32) -> bool {
- let (x, y, w, h) = self.rect();
- let hx = if self.row_w > 0.0 { self.row_x } else { x };
- let hw = if self.row_w > 0.0 { self.row_w } else { w };
- let (hy, hh) = if self.label.is_some() {
- (y - 18.0, h + 18.0)
- } else {
- (y, h)
- };
- px >= hx && px <= hx + hw && py >= hy && py <= hy + hh
- }
-
- fn top_room(&self) -> f32 { if self.label.is_some() { 18.0 } else { 0.0 } }
-
- fn cursor_moved(&mut self, px: f32, py: f32) -> bool {
- if self.disabled {
- let was = self.hovered;
- self.hovered = false;
- return was;
- }
- let was = self.hovered;
- self.hovered = self.hit_test(px, py);
- was != self.hovered
- }
-
- fn mouse_input(&mut self, button: MouseButton, state: ElementState, px: f32, py: f32) -> bool {
- if self.disabled { return false; }
- if button != MouseButton::Left { return false; }
- if state != ElementState::Pressed { return false; }
- let (x, y, w, h) = self.rect();
- let (hy, hh) = if self.label.is_some() {
- (y - 18.0, h + 18.0)
- } else {
- (y, h)
- };
- if !(px >= x && px <= x + w && py >= hy && py <= hy + hh) {
- return false;
- }
- self.focus();
- true
- }
-
- fn focus(&mut self) {
- if self.disabled { return; }
- if !self.editing {
- self.editing = true;
- self.edit_buffer = self.text.clone();
- clear_ui::widget::focus::set_focused(self);
- }
- }
-
- fn unfocus(&mut self) {
- if self.editing {
- self.editing = false;
- if self.text != self.edit_buffer {
- self.text = self.edit_buffer.clone();
- self.just_changed = true;
- }
- }
- }
-
- fn keyboard_input(&mut self, event: &KeyEvent) -> bool {
- if self.disabled { return false; }
- if !self.editing { return false; }
- if event.state != ElementState::Pressed { return false; }
- match &event.logical_key {
- Key::Named(NamedKey::Backspace) => {
- self.edit_buffer.pop();
- true
- }
- Key::Named(NamedKey::Enter) => {
- self.text = self.edit_buffer.clone();
- self.editing = false;
- self.just_changed = true;
- true
- }
- Key::Named(NamedKey::Escape) => {
- self.editing = false;
- true
- }
- _ => {
- if let Some(text) = &event.text {
- if !event.repeat {
- for ch in text.chars() {
- if ch.is_alphanumeric() || ch == ' ' || ch == '-' || ch == '_' || ch == '*' {
- self.edit_buffer.push(ch);
- }
- }
- }
- }
- true
- }
- }
- }
-
- fn hover_highlight(&self) -> Option<[f32; 4]> {
- None
- }
-
- fn extra_quads(&self) -> Vec<(f32, f32, f32, f32, [f32; 4])> {
- let mut quads = Vec::new();
- if self.disabled {
- quads.push((self.x, self.y, self.w, self.h, [0.12, 0.12, 0.16, 1.0])); // border
- quads.push((self.x + 1.0, self.y + 1.0, self.w - 2.0, self.h - 2.0, [0.06, 0.06, 0.08, 1.0])); // bg
- return quads;
- }
- if self.hovered {
- let (hy, hh) = if self.label.is_some() {
- (self.y - 18.0, self.h + 18.0)
- } else {
- (self.y, self.h)
- };
- let hx = if self.row_w > 0.0 { self.row_x } else { self.x };
- let hw = if self.row_w > 0.0 { self.row_w } else { self.w };
- quads.push((hx, hy, hw, hh, [1.0, 1.0, 1.0, 0.06]));
- }
- let bg_color = if self.editing {
- [0.12, 0.12, 0.18, 1.0]
- } else {
- [0.08, 0.08, 0.12, 1.0]
- };
- let border_color = if self.editing {
- [0.30, 0.50, 0.32, 1.0]
- } else if self.hovered {
- [0.25, 0.25, 0.35, 1.0]
- } else {
- [0.18, 0.18, 0.24, 1.0]
- };
- quads.push((self.x, self.y, self.w, self.h, border_color));
- quads.push((self.x + 1.0, self.y + 1.0, self.w - 2.0, self.h - 2.0, bg_color));
- quads
- }
-
- fn text_labels(&self) -> Vec<TextLabel> {
- let mut labels = Vec::new();
- if let Some(ref label) = self.label {
- labels.push(TextLabel {
- text: label.clone(),
- x: self.x + 4.0,
- y: self.y - 14.0,
- font_size: 12.0,
- color: [0x83, 0x83, 0x8a],
- });
- }
- let val_text = if self.editing {
- format!("{}|", self.edit_buffer)
- } else {
- self.text.clone()
- };
- labels.push(TextLabel {
- text: val_text,
- x: self.x + 8.0,
- y: self.y + (self.h - 12.0) / 2.0,
- font_size: 13.0,
- color: if self.disabled { [0x53, 0x53, 0x5a] } else if self.editing { [0xee, 0xee, 0xf5] } else { [0xcc, 0xcc, 0xd4] },
- });
- labels
- }
-
- fn parent(&self) -> Option<*mut (dyn Widget + 'static)> { self.parent }
- fn set_parent(&mut self, parent: Option<*mut (dyn Widget + 'static)>) { self.parent = parent; }
- fn children(&self) -> Vec<*mut (dyn Widget + 'static)> { self.children.clone() }
- fn add_child(&mut self, child: *mut (dyn Widget + 'static)) { self.children.push(child); }
- fn clear_children(&mut self) { self.children.clear(); }
-}
-
-impl Drop for TextBox {
- fn drop(&mut self) {
- clear_ui::widget::focus::clear_if_matches(self);
- }
-}
-
-unsafe impl Send for TextBox {}
-unsafe impl Sync for TextBox {}
-
// ── TypefaceState and TypefaceMessage ──
#[derive(Debug, Clone)]
@@ -278,6 +34,10 @@ pub struct TypefaceState {
pub status_menu: Dropdown,
pub fuzzel_menu: Dropdown,
pub terminal_menu: Dropdown,
+ pub borders_size_box: Spinbox,
+ pub status_size_box: Spinbox,
+ pub fuzzel_size_box: Spinbox,
+ pub terminal_size_box: Spinbox,
}
impl Default for TypefaceState {
@@ -307,6 +67,10 @@ impl Default for TypefaceState {
status_menu: Dropdown::default(),
fuzzel_menu: Dropdown::default(),
terminal_menu: Dropdown::default(),
+ borders_size_box: Spinbox::new(11, 6, 72, 1),
+ status_size_box: Spinbox::new(13, 6, 72, 1),
+ fuzzel_size_box: Spinbox::new(14, 6, 72, 1),
+ terminal_size_box: Spinbox::new(12, 6, 72, 1),
}
}
}
@@ -328,6 +92,10 @@ pub enum TypefaceMessage {
SetStatusMenu(usize),
SetFuzzelMenu(usize),
SetTerminalMenu(usize),
+ SetBordersSize(i32),
+ SetStatusSize(i32),
+ SetFuzzelSize(i32),
+ SetTerminalSize(i32),
}
fn parse_font_for_alias(content: &str, alias: &str) -> Option<String> {
@@ -469,6 +237,164 @@ pub fn save_preferred_fonts(
.spawn();
}
+fn parse_u16_from(content: &str, key: &str, default: u16) -> u16 {
+ for line in content.lines() {
+ let trimmed = line.trim();
+ if let Some(rest) = trimmed.strip_prefix(key) {
+ let rest = rest.trim_start_matches(|c: char| c == ' ' || c == '=' || c == '"');
+ return rest.trim_end_matches('"').trim().parse::<u16>().unwrap_or(default);
+ }
+ }
+ default
+}
+
+fn write_config_value(key: &str, value: &str) -> bool {
+ let content = fs::read_to_string("/home/lsgalante/.config/clearwm/config.toml").unwrap_or_default();
+ let new_line = format!("{} = {}", key, value);
+ let mut found = false;
+ let updated: String = content.lines()
+ .map(|line| {
+ if line.trim().starts_with(key) { found = true; new_line.clone() }
+ else { line.to_string() }
+ }).collect::<Vec<_>>().join("\n");
+ if !found {
+ let mut result = String::new();
+ let mut in_layout = false;
+ let mut inserted = false;
+ for line in updated.lines() {
+ if line.trim() == "[layout]" { in_layout = true; }
+ else if line.trim().starts_with('[') && in_layout {
+ if !inserted { result.push_str(&new_line); result.push('\n'); inserted = true; }
+ in_layout = false;
+ }
+ result.push_str(line); result.push('\n');
+ }
+ if in_layout && !inserted { result.push_str(&new_line); result.push('\n'); }
+ fs::write("/home/lsgalante/.config/clearwm/config.toml", result).is_ok()
+ } else { fs::write("/home/lsgalante/.config/clearwm/config.toml", updated).is_ok() }
+}
+
+fn send_ipc_command(cmd: &str) {
+ if let Ok(mut stream) = std::os::unix::net::UnixStream::connect("/tmp/clearwm.sock") {
+ use std::io::Write;
+ let _ = stream.write_all(format!("{}\n", cmd).as_bytes());
+ }
+}
+
+fn read_border_font_size() -> Option<u16> {
+ let content = fs::read_to_string("/home/lsgalante/.config/clearwm/config.toml").ok()?;
+ Some(parse_u16_from(&content, "border_font_size", 11))
+}
+
+fn read_waybar_size() -> Option<u16> {
+ let css = fs::read_to_string("/home/lsgalante/.config/waybar/style.css").ok()?;
+ for line in css.lines() {
+ if line.contains("font-size") {
+ let val = line.split(':').nth(1)?.trim().trim_end_matches(';').trim();
+ if let Some(px) = val.strip_suffix("px") {
+ return px.trim().parse::<u16>().ok();
+ }
+ return val.parse::<u16>().ok();
+ }
+ }
+ None
+}
+
+fn write_waybar_size(size: u16) {
+ let path = "/home/lsgalante/.config/waybar/style.css";
+ let css = fs::read_to_string(path).unwrap_or_default();
+ let mut new_lines = Vec::new();
+ for line in css.lines() {
+ if line.contains("font-size") {
+ new_lines.push(format!(" font-size: {}px;", size));
+ } else {
+ new_lines.push(line.to_string());
+ }
+ }
+ let _ = fs::write(path, new_lines.join("\n"));
+ let _ = std::process::Command::new("pkill")
+ .args(["-x", "waybar", "-SIGUSR2"])
+ .spawn();
+}
+
+fn read_fuzzel_size() -> Option<u16> {
+ let ini = fs::read_to_string("/home/lsgalante/.config/fuzzel/fuzzel.ini").ok()?;
+ for line in ini.lines() {
+ let trimmed = line.trim();
+ if trimmed.starts_with("font") {
+ if let Some(pos) = trimmed.find("size=") {
+ let size_str = &trimmed[pos + 5..];
+ let end_pos = size_str.find(|c: char| !c.is_ascii_digit()).unwrap_or(size_str.len());
+ return size_str[..end_pos].parse::<u16>().ok();
+ }
+ }
+ }
+ None
+}
+
+fn write_fuzzel_size(size: u16) {
+ let path = "/home/lsgalante/.config/fuzzel/fuzzel.ini";
+ let ini = fs::read_to_string(path).unwrap_or_default();
+ let mut new_lines = Vec::new();
+ for line in ini.lines() {
+ let trimmed = line.trim();
+ if trimmed.starts_with("font") {
+ if let Some(pos) = line.find("size=") {
+ let mut new_line = line[..pos + 5].to_string();
+ new_line.push_str(&size.to_string());
+ let size_str = &line[pos + 5..];
+ let skip = size_str.find(|c: char| !c.is_ascii_digit()).unwrap_or(size_str.len());
+ new_line.push_str(&size_str[skip..]);
+ new_lines.push(new_line);
+ } else {
+ new_lines.push(line.to_string());
+ }
+ } else {
+ new_lines.push(line.to_string());
+ }
+ }
+ let _ = fs::write(path, new_lines.join("\n"));
+}
+
+fn read_terminal_size() -> Option<u16> {
+ let ini = fs::read_to_string("/home/lsgalante/.config/foot/foot.ini").ok()?;
+ for line in ini.lines() {
+ let trimmed = line.trim();
+ if trimmed.starts_with("font") {
+ if let Some(pos) = trimmed.find("terminal:size=") {
+ let size_str = &trimmed[pos + 14..];
+ let end_pos = size_str.find(|c: char| !c.is_ascii_digit()).unwrap_or(size_str.len());
+ return size_str[..end_pos].parse::<u16>().ok();
+ }
+ }
+ }
+ None
+}
+
+fn write_terminal_size(size: u16) {
+ let path = "/home/lsgalante/.config/foot/foot.ini";
+ let ini = fs::read_to_string(path).unwrap_or_default();
+ let mut new_lines = Vec::new();
+ for line in ini.lines() {
+ let trimmed = line.trim();
+ if trimmed.starts_with("font") {
+ if let Some(pos) = line.find("terminal:size=") {
+ let mut new_line = line[..pos + 14].to_string();
+ new_line.push_str(&size.to_string());
+ let size_str = &line[pos + 14..];
+ let skip = size_str.find(|c: char| !c.is_ascii_digit()).unwrap_or(size_str.len());
+ new_line.push_str(&size_str[skip..]);
+ new_lines.push(new_line);
+ } else {
+ new_lines.push(line.to_string());
+ }
+ } else {
+ new_lines.push(line.to_string());
+ }
+ }
+ let _ = fs::write(path, new_lines.join("\n"));
+}
+
fn parse_families(output: Option<std::process::Output>) -> Vec<String> {
let mut families = Vec::new();
if let Some(o) = output {
@@ -526,18 +452,23 @@ pub async fn fetch_typeface_state() -> TypefaceState {
"Other".to_string(),
];
- let mut borders_box = TextBox::new(borders.clone()).with_label("Window Borders");
+ let mut borders_box = TextBox::new(borders.clone()).with_label("Window Borders").with_width(300.0);
borders_box.disabled = borders_idx != 3;
- let mut status_box = TextBox::new(status.clone()).with_label("Status Interface");
+ let mut status_box = TextBox::new(status.clone()).with_label("Status Interface").with_width(300.0);
status_box.disabled = status_idx != 3;
- let mut fuzzel_box = TextBox::new(fuzzel_font.clone()).with_label("Fuzzel");
+ let mut fuzzel_box = TextBox::new(fuzzel_font.clone()).with_label("Fuzzel").with_width(300.0);
fuzzel_box.disabled = fuzzel_idx != 3;
- let mut terminal_box = TextBox::new(term.clone()).with_label("Terminal");
+ let mut terminal_box = TextBox::new(term.clone()).with_label("Terminal").with_width(300.0);
terminal_box.disabled = terminal_idx != 3;
+ let borders_size = read_border_font_size().unwrap_or(11);
+ let status_size = read_waybar_size().unwrap_or(13);
+ let fuzzel_size = read_fuzzel_size().unwrap_or(14);
+ let terminal_size = read_terminal_size().unwrap_or(12);
+
TypefaceState {
loaded: true,
sans_serif: sans.clone(),
@@ -563,6 +494,10 @@ pub async fn fetch_typeface_state() -> TypefaceState {
status_menu: Dropdown::new(menu_options.clone(), status_idx),
fuzzel_menu: Dropdown::new(menu_options.clone(), fuzzel_idx),
terminal_menu: Dropdown::new(menu_options.clone(), terminal_idx),
+ borders_size_box: Spinbox::new(borders_size as i32, 6, 72, 1),
+ status_size_box: Spinbox::new(status_size as i32, 6, 72, 1),
+ fuzzel_size_box: Spinbox::new(fuzzel_size as i32, 6, 72, 1),
+ terminal_size_box: Spinbox::new(terminal_size as i32, 6, 72, 1),
}
}
@@ -607,42 +542,59 @@ pub fn view(state: &mut TypefaceState, cx: f32, cy: f32, cw: f32, _ch: f32, sec_
sec.spacing(18.0);
} else {
let dropdown_w = 120.0;
- let textbox_w = widget_w - dropdown_w - 12.0;
+ let textbox_w = 300.0;
+ let spinbox_w = 90.0;
+
+ let menu_row_w = dropdown_w + 10.0;
+ let box_row_w = textbox_w + 10.0;
+ let spinbox_row_w = spinbox_w + 10.0;
+
+ let box_row_x = cx + 8.0 + menu_row_w;
+ let spin_row_x = box_row_x + box_row_w;
+ let spin_x = cx + 12.0 + dropdown_w + 12.0 + textbox_w + 12.0;
// Window Borders
let start_y = sec.ay();
let top_room = state.borders_box.top_room();
- state.borders_menu.set_row_rect(cx + 8.0, cw - 16.0);
+ state.borders_menu.set_row_rect(cx + 8.0, menu_row_w);
clear_ui::layout::render_widget(&mut pc, &mut state.borders_menu, cx + 12.0, start_y + top_room, dropdown_w, widget_h);
- state.borders_box.set_row_rect(cx + 8.0, cw - 16.0);
+ state.borders_box.set_row_rect(box_row_x, box_row_w);
clear_ui::layout::render_widget(&mut pc, &mut state.borders_box, cx + 12.0 + dropdown_w + 12.0, start_y + top_room, textbox_w, widget_h);
+ state.borders_size_box.set_row_rect(spin_row_x, spinbox_row_w);
+ clear_ui::layout::render_widget(&mut pc, &mut state.borders_size_box, spin_x, start_y + top_room, spinbox_w, widget_h);
sec.spacing(widget_h + top_room + 12.0);
// Status Interface
let start_y = sec.ay();
let top_room = state.status_box.top_room();
- state.status_menu.set_row_rect(cx + 8.0, cw - 16.0);
+ state.status_menu.set_row_rect(cx + 8.0, menu_row_w);
clear_ui::layout::render_widget(&mut pc, &mut state.status_menu, cx + 12.0, start_y + top_room, dropdown_w, widget_h);
- state.status_box.set_row_rect(cx + 8.0, cw - 16.0);
+ state.status_box.set_row_rect(box_row_x, box_row_w);
clear_ui::layout::render_widget(&mut pc, &mut state.status_box, cx + 12.0 + dropdown_w + 12.0, start_y + top_room, textbox_w, widget_h);
+ state.status_size_box.set_row_rect(spin_row_x, spinbox_row_w);
+ clear_ui::layout::render_widget(&mut pc, &mut state.status_size_box, spin_x, start_y + top_room, spinbox_w, widget_h);
sec.spacing(widget_h + top_room + 12.0);
// Fuzzel
let start_y = sec.ay();
let top_room = state.fuzzel_box.top_room();
- state.fuzzel_menu.set_row_rect(cx + 8.0, cw - 16.0);
+ state.fuzzel_menu.set_row_rect(cx + 8.0, menu_row_w);
clear_ui::layout::render_widget(&mut pc, &mut state.fuzzel_menu, cx + 12.0, start_y + top_room, dropdown_w, widget_h);
- state.fuzzel_box.set_row_rect(cx + 8.0, cw - 16.0);
+ state.fuzzel_box.set_row_rect(box_row_x, box_row_w);
clear_ui::layout::render_widget(&mut pc, &mut state.fuzzel_box, cx + 12.0 + dropdown_w + 12.0, start_y + top_room, textbox_w, widget_h);
+ state.fuzzel_size_box.set_row_rect(spin_row_x, spinbox_row_w);
+ clear_ui::layout::render_widget(&mut pc, &mut state.fuzzel_size_box, spin_x, start_y + top_room, spinbox_w, widget_h);
sec.spacing(widget_h + top_room + 12.0);
// Terminal
let start_y = sec.ay();
let top_room = state.terminal_box.top_room();
- state.terminal_menu.set_row_rect(cx + 8.0, cw - 16.0);
+ state.terminal_menu.set_row_rect(cx + 8.0, menu_row_w);
clear_ui::layout::render_widget(&mut pc, &mut state.terminal_menu, cx + 12.0, start_y + top_room, dropdown_w, widget_h);
- state.terminal_box.set_row_rect(cx + 8.0, cw - 16.0);
+ state.terminal_box.set_row_rect(box_row_x, box_row_w);
clear_ui::layout::render_widget(&mut pc, &mut state.terminal_box, cx + 12.0 + dropdown_w + 12.0, start_y + top_room, textbox_w, widget_h);
+ state.terminal_size_box.set_row_rect(spin_row_x, spinbox_row_w);
+ clear_ui::layout::render_widget(&mut pc, &mut state.terminal_size_box, spin_x, start_y + top_room, spinbox_w, widget_h);
sec.spacing(widget_h + top_room + 8.0);
}
let prog_focused = sec_focused.get(1).copied().unwrap_or(false);
@@ -940,6 +892,10 @@ pub fn update(state: &mut TypefaceState, msg: TypefaceMessage) {
if !state.search_box.editing {
state.search_box = new.search_box;
}
+ state.borders_size_box = new.borders_size_box;
+ state.status_size_box = new.status_size_box;
+ state.fuzzel_size_box = new.fuzzel_size_box;
+ state.terminal_size_box = new.terminal_size_box;
let old_scroll = state.list_box.scroll_y();
state.list_box = new.list_box;
state.list_box.set_scroll_y(old_scroll);
@@ -1095,16 +1051,32 @@ pub fn update(state: &mut TypefaceState, msg: TypefaceMessage) {
let text = font.clone();
move || {
let mut copied = false;
- if let Ok(mut child) = std::process::Command::new("wl-copy")
+ let child = std::process::Command::new("wl-copy")
.stdin(std::process::Stdio::piped())
- .spawn()
- {
- if let Some(mut stdin) = child.stdin.take() {
- if stdin.write_all(text.as_bytes()).is_ok() {
- copied = true;
+ .stderr(std::process::Stdio::piped())
+ .spawn();
+ match child {
+ Ok(mut child) => {
+ if let Some(mut stdin) = child.stdin.take() {
+ let _ = stdin.write_all(text.as_bytes());
}
+ match child.wait_with_output() {
+ Ok(output) => {
+ if output.status.success() {
+ copied = true;
+ } else {
+ let err_msg = String::from_utf8_lossy(&output.stderr);
+ eprintln!("wl-copy exited with error status: {:?}, stderr: {}", output.status, err_msg);
+ }
+ }
+ Err(e) => {
+ eprintln!("wl-copy wait failed: {:?}", e);
+ }
+ }
+ }
+ Err(e) => {
+ eprintln!("wl-copy spawn failed: {:?}", e);
}
- let _ = child.wait();
}
if !copied {
if let Ok(mut child) = std::process::Command::new("xclip")
@@ -1214,6 +1186,23 @@ pub fn update(state: &mut TypefaceState, msg: TypefaceMessage) {
&state.terminal,
);
}
+ TypefaceMessage::SetBordersSize(val) => {
+ state.borders_size_box.value = val;
+ write_config_value("border_font_size", &val.to_string());
+ send_ipc_command(&format!("layout border_font_size {}", val));
+ }
+ TypefaceMessage::SetStatusSize(val) => {
+ state.status_size_box.value = val;
+ write_waybar_size(val as u16);
+ }
+ TypefaceMessage::SetFuzzelSize(val) => {
+ state.fuzzel_size_box.value = val;
+ write_fuzzel_size(val as u16);
+ }
+ TypefaceMessage::SetTerminalSize(val) => {
+ state.terminal_size_box.value = val;
+ write_terminal_size(val as u16);
+ }
}
}