From fadaa1723474b498039c0234009cf47b4cdfdd3e Mon Sep 17 00:00:00 2001 From: poisnada Date: Tue, 24 Jun 2025 09:44:32 -0300 Subject: [PATCH 1/5] chore: get first item over cursor on drag --- src/systems/drag.rs | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/src/systems/drag.rs b/src/systems/drag.rs index 9d1993f..46cdd71 100644 --- a/src/systems/drag.rs +++ b/src/systems/drag.rs @@ -32,17 +32,26 @@ pub fn drag_check_system( if let Some(world_cursor) = camera.viewport_to_world_2d(camera_transform, cursor_pos) { - for (entity, hover, transform) in q_icons.iter() { + let mut top_dragged: Option<(Entity, f32, Vec2)> = None; + + for (entity, _hover, transform) in q_icons.iter() { let pos = transform.translation.truncate(); - let size = Vec2::splat(config.icon_size * hover.original_scale); + let size = Vec2::splat(config.icon_size * transform.scale.x); let rect = Rect::from_center_size(pos, size * 1.1); + if rect.contains(world_cursor) { - let offset = world_cursor - pos; - commands.entity(entity).insert(Dragging { offset }); - ui_state.dragging = Some(entity); - break; + let z = transform.translation.z; + if top_dragged.is_none() || z > top_dragged.unwrap().1 { + top_dragged = Some((entity, z, pos)); + } } } + + if let Some((entity, _z, pos)) = top_dragged { + let offset = world_cursor - pos; + commands.entity(entity).insert(Dragging { offset }); + ui_state.dragging = Some(entity); + } } } } From 2d21d96a3eef54d24d2e3e95e594277f0da12db9 Mon Sep 17 00:00:00 2001 From: poisnada Date: Tue, 24 Jun 2025 09:45:03 -0300 Subject: [PATCH 2/5] fix: chage scale on hover --- src/utils/hover.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/hover.rs b/src/utils/hover.rs index ce0d67f..804f2e4 100644 --- a/src/utils/hover.rs +++ b/src/utils/hover.rs @@ -33,7 +33,7 @@ pub fn hover_system( let mut top_hovered: Option<(usize, f32)> = None; for (i, (_hover_target, transform)) in q_icons.iter().enumerate() { let icon_position = transform.translation.truncate(); - let size = Vec2::splat(config.icon_size); + let size = Vec2::splat(config.icon_size * transform.scale.x); let rect = Rect::from_center_size(icon_position, size * 1.1); if rect.contains(world_pos) { let z = transform.translation.z; @@ -121,7 +121,7 @@ pub fn hover_animation_system( let delta_time = time.delta_seconds(); - let scroll = scroll_state.total_scroll_distance / config.spacing; + let scroll = scroll_state.offset.x; let first_visible_index = scroll.floor() as usize; let interp = scroll - scroll.floor(); From 69ac70a7aba4b37cab859913962dd388d351f9d6 Mon Sep 17 00:00:00 2001 From: poisnada Date: Tue, 24 Jun 2025 09:54:25 -0300 Subject: [PATCH 3/5] ci: update version --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index dd7add3..fe59e53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "anny-dock" -version = "0.0.2" +version = "0.0.3" edition = "2021" description = "A modern, animated dock for Hyprland built with Rust and Bevy Engine" readme = "README.md" From 6d5bcf1373d6f6a2005a5a0facce9cbc931a1bcb Mon Sep 17 00:00:00 2001 From: poisnada Date: Tue, 24 Jun 2025 09:56:50 -0300 Subject: [PATCH 4/5] feat: move launcher logic to a specific file --- src/utils/hover.rs | 24 +++---- src/utils/launcher.rs | 144 +++++++++++++++++++++++++++++++++++++++++ src/utils/mod.rs | 147 +----------------------------------------- 3 files changed, 159 insertions(+), 156 deletions(-) create mode 100644 src/utils/launcher.rs diff --git a/src/utils/hover.rs b/src/utils/hover.rs index 804f2e4..7e1cc6d 100644 --- a/src/utils/hover.rs +++ b/src/utils/hover.rs @@ -14,6 +14,16 @@ const HOVER_LIFT: f32 = 15.0; const HOVER_SCALE: f32 = 1.15; const ANIMATION_SMOOTHNESS: f32 = 0.85; +fn reset_hover_state(q_icons: &mut Query<(&mut HoverTarget, &Transform)>) { + for (mut hover, _) in q_icons.iter_mut() { + if hover.is_hovered && hover.hover_exit_timer.is_none() { + hover.hover_exit_timer = + Some(Timer::new(Duration::from_secs_f32(0.15), TimerMode::Once)); + } + hover.is_hovered = false; + } +} + pub fn hover_system( windows: Query<&Window, With>, q_camera: Query<(&Camera, &GlobalTransform), With>, @@ -57,20 +67,10 @@ pub fn hover_system( } } } else { - for (mut hover, _) in &mut q_icons { - if hover.is_hovered && hover.hover_exit_timer.is_none() { - hover.hover_exit_timer = Some(Timer::new(Duration::from_secs_f32(0.15), TimerMode::Once)); - } - hover.is_hovered = false; - } + reset_hover_state(&mut q_icons); } } else { - for (mut hover, _) in &mut q_icons { - if hover.is_hovered && hover.hover_exit_timer.is_none() { - hover.hover_exit_timer = Some(Timer::new(Duration::from_secs_f32(0.15), TimerMode::Once)); - } - hover.is_hovered = false; - } + reset_hover_state(&mut q_icons); } } diff --git a/src/utils/launcher.rs b/src/utils/launcher.rs new file mode 100644 index 0000000..d55ffc2 --- /dev/null +++ b/src/utils/launcher.rs @@ -0,0 +1,144 @@ +use bevy::log::{error, info, warn}; +use std::fs; +use std::process::Command; + +pub fn launch_application(class: &str) { + match find_exec_for_class(class) { + Some(exec) => { + info!("Found executable: {}", exec); + let clean_exec = exec.split_whitespace() + .take_while(|&part| !part.starts_with('%')) + .collect::>() + .join(" "); + + info!("Launching with cleaned exec: {}", clean_exec); + + let direct_output = Command::new("sh") + .arg("-c") + .arg(&clean_exec) + .spawn(); + + match direct_output { + Ok(_) => { + info!("Successfully launched application: {}", class); + } + Err(e) => { + error!("Failed to launch directly, trying fallback: {:?}", e); + let fallback = Command::new("hyprctl") + .args(["dispatch", "exec", class]) + .spawn(); + + match fallback { + Ok(_) => info!("Successfully launched via hyprctl: {}", class), + Err(e) => error!("All launch attempts failed: {:?}", e), + } + } + } + } + _ => { + warn!("No executable found for class: {}, trying direct launch", class); + let output = Command::new("hyprctl") + .args(["dispatch", "exec", class]) + .spawn(); + + match output { + Ok(_) => info!("Successfully launched via hyprctl: {}", class), + Err(e) => error!("Failed to launch application: {:?}", e), + } + } + } +} + +fn find_exec_for_class(class: &str) -> Option { + // refator needed + let binds = dirs::data_local_dir()?.join("applications/"); + let dirs = [ + "/usr/share/applications/", + "/usr/local/share/applications/", + binds.to_str()?, + ]; + + for dir in dirs.iter() { + let path = std::path::Path::new(dir).join(format!("{class}.desktop")); + if path.exists() { + if let Ok(content) = fs::read_to_string(&path) { + if let Some(exec) = parse_exec_only(&content) { + return Some(exec); + } + } + } + } + + for dir in dirs.iter() { + if let Ok(entries) = fs::read_dir(dir) { + for entry in entries.flatten() { + let file_path = entry.path(); + if file_path.extension().and_then(|s| s.to_str()) == Some("desktop") { + if let Ok(content) = fs::read_to_string(&file_path) { + if let Some(exec) = parse_desktop_file(&content, class) { + return Some(exec); + } + } + } + } + } + } + + None +} + +fn parse_exec_only(content: &str) -> Option { + let mut in_desktop_entry = false; + let mut exec = None; + let mut no_display = false; + let mut terminal = false; + + for line in content.lines() { + let line = line.trim(); + if line.starts_with('[') { + in_desktop_entry = line == "[Desktop Entry]"; + } else if in_desktop_entry { + if let Some((key, value)) = line.split_once('=') { + match key.trim() { + "Exec" => exec = Some(value.trim().to_string()), + "NoDisplay" => no_display = value.trim().to_lowercase() == "true", + "Terminal" => terminal = value.trim().to_lowercase() == "true", + _ => {} + } + } + } + } + + if no_display || terminal { + return None; + } + + exec +} + +fn parse_desktop_file(content: &str, class: &str) -> Option { + let mut in_desktop_entry = false; + let mut startup_wm_class = None; + let mut exec = None; + + for line in content.lines() { + let line = line.trim(); + if line.starts_with('[') { + in_desktop_entry = line == "[Desktop Entry]"; + } else if in_desktop_entry { + if let Some((key, value)) = line.split_once('=') { + match key.trim() { + "StartupWMClass" => startup_wm_class = Some(value.trim()), + "Exec" => exec = Some(value.trim()), + _ => {} + } + } + } + } + + if startup_wm_class == Some(class) { + exec.map(|s| s.to_string()) + } else { + None + } +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index 53c4ac9..0ffcf23 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -2,154 +2,13 @@ pub use loader::*; pub mod hover; pub mod loader; +pub mod launcher; +pub use launcher::*; + use crate::config::Config; -use bevy::log::{error, info, warn}; use bevy::math::{Vec2, Vec3}; -use std::fs; -use std::process::Command; use bevy::prelude::*; -pub fn launch_application(class: &str) { - match find_exec_for_class(class) { - Some(exec) => { - info!("Found executable: {}", exec); - let clean_exec = exec.split_whitespace() - .take_while(|&part| !part.starts_with('%')) - .collect::>() - .join(" "); - - info!("Launching with cleaned exec: {}", clean_exec); - - let direct_output = Command::new("sh") - .arg("-c") - .arg(&clean_exec) - .spawn(); - - match direct_output { - Ok(_) => { - info!("Successfully launched application: {}", class); - } - Err(e) => { - error!("Failed to launch directly, trying fallback: {:?}", e); - let fallback = Command::new("hyprctl") - .args(["dispatch", "exec", class]) - .spawn(); - - match fallback { - Ok(_) => info!("Successfully launched via hyprctl: {}", class), - Err(e) => error!("All launch attempts failed: {:?}", e), - } - } - } - } - _ => { - warn!("No executable found for class: {}, trying direct launch", class); - let output = Command::new("hyprctl") - .args(["dispatch", "exec", class]) - .spawn(); - - match output { - Ok(_) => info!("Successfully launched via hyprctl: {}", class), - Err(e) => error!("Failed to launch application: {:?}", e), - } - } - } -} - -fn find_exec_for_class(class: &str) -> Option { - // refator needed - let binds = dirs::data_local_dir()?.join("applications/"); - let dirs = [ - "/usr/share/applications/", - "/usr/local/share/applications/", - binds.to_str()?, - ]; - - for dir in dirs.iter() { - let path = std::path::Path::new(dir).join(format!("{class}.desktop")); - if path.exists() { - if let Ok(content) = fs::read_to_string(&path) { - if let Some(exec) = parse_exec_only(&content) { - return Some(exec); - } - } - } - } - - for dir in dirs.iter() { - if let Ok(entries) = fs::read_dir(dir) { - for entry in entries.flatten() { - let file_path = entry.path(); - if file_path.extension().and_then(|s| s.to_str()) == Some("desktop") { - if let Ok(content) = fs::read_to_string(&file_path) { - if let Some(exec) = parse_desktop_file(&content, class) { - return Some(exec); - } - } - } - } - } - } - - None -} - -fn parse_exec_only(content: &str) -> Option { - let mut in_desktop_entry = false; - let mut exec = None; - let mut no_display = false; - let mut terminal = false; - - for line in content.lines() { - let line = line.trim(); - if line.starts_with('[') { - in_desktop_entry = line == "[Desktop Entry]"; - } else if in_desktop_entry { - if let Some((key, value)) = line.split_once('=') { - match key.trim() { - "Exec" => exec = Some(value.trim().to_string()), - "NoDisplay" => no_display = value.trim().to_lowercase() == "true", - "Terminal" => terminal = value.trim().to_lowercase() == "true", - _ => {} - } - } - } - } - - if no_display || terminal { - return None; - } - - exec -} - -fn parse_desktop_file(content: &str, class: &str) -> Option { - let mut in_desktop_entry = false; - let mut startup_wm_class = None; - let mut exec = None; - - for line in content.lines() { - let line = line.trim(); - if line.starts_with('[') { - in_desktop_entry = line == "[Desktop Entry]"; - } else if in_desktop_entry { - if let Some((key, value)) = line.split_once('=') { - match key.trim() { - "StartupWMClass" => startup_wm_class = Some(value.trim()), - "Exec" => exec = Some(value.trim()), - _ => {} - } - } - } - } - - if startup_wm_class == Some(class) { - exec.map(|s| s.to_string()) - } else { - None - } -} - #[derive(Resource)] pub struct IconAnimationState { pub _is_scrolling: bool, From 955ab2420d73d465a9368ae244edb392baab2822 Mon Sep 17 00:00:00 2001 From: poisnada Date: Tue, 24 Jun 2025 10:37:45 -0300 Subject: [PATCH 5/5] feat(ui): Rework icon titles to fix flickering and improve stability This commit resolves a persistent UI bug where icon titles would flicker and jitter when their visibility was toggled (via the 'T' key) or during hover animations. The root cause was two conflicting systems updating transforms independently: one for icon hover animations and another manually setting text positions. This led to synchronization issues and visual instability. The solution was to refactor the UI to use Bevy's parent-child entity hierarchy: - Icon title text entities are now spawned as children of their corresponding icon entity. - This leverages Bevy's transform propagation, ensuring titles automatically and smoothly follow their parent icon's position, rotation, and scale. - The obsolete system for manual text position updates has been removed. Additional improvements in this commit: - **System Order:** Introduced s (, , ) to enforce a clear and predictable execution order. - **Hover/Drag:** Improved hover detection to respect icon scaling and fixed drag-and-drop to correctly select the topmost icon. - **Modularity:** Extracted application launcher logic into a dedicated module. - **Config:** Added a field to the configuration to control title text size. --- src/components/favorites.rs | 105 ++++++++++++++++++------------------ src/config.rs | 2 + src/main.rs | 100 ++++++++++++++++------------------ src/systems/icon.rs | 29 +--------- src/systems/title.rs | 31 ++++------- src/types.rs | 7 +-- 6 files changed, 113 insertions(+), 161 deletions(-) diff --git a/src/components/favorites.rs b/src/components/favorites.rs index 3b7b894..e1e5c72 100644 --- a/src/components/favorites.rs +++ b/src/components/favorites.rs @@ -13,7 +13,8 @@ pub struct Favorites(pub Vec); #[derive(Component, Debug)] pub struct Favorite; -use crate::{config::Config, ClientAddress, IconText}; +use crate::config::Config; +use crate::types::{ClientAddress, IconTitleText}; pub(crate) fn add_client_address(commands: &mut Commands, entity: Entity, address: String) { commands.entity(entity).insert(ClientAddress(address)); @@ -31,39 +32,66 @@ pub(crate) fn add_favorite( pub(crate) fn add_icon_text( commands: &mut Commands, - entity: Entity, + parent_icon: Entity, class: &str, - transform: Transform, - scale: f32, _asset_server: &AssetServer, config: &Res, ) { const TEXT_OFFSET: f32 = 8.0; - commands - .spawn(Text2dBundle { - text: Text::from_section( - class.to_string(), - TextStyle { - // font: asset_server.load(FONT_PATH), - font: TextStyle::default().font, - font_size: 8.0 * scale, - color: Color::WHITE, + commands.entity(parent_icon).with_children(|parent| { + parent.spawn(( + Text2dBundle { + text: Text::from_section( + class.to_string(), + TextStyle { + font: TextStyle::default().font, + font_size: config.font_size, + color: Color::WHITE, + }, + ) + .with_alignment(TextAlignment::Center), + transform: Transform { + translation: Vec3::new( + 0.0, + -(config.icon_size / 2.0) - TEXT_OFFSET, + 0.1, + ), + ..default() }, - ) - .with_alignment(TextAlignment::Center), - transform: Transform { - translation: Vec3::new( - transform.translation.x, - transform.translation.y - (config.icon_size * scale / 2.0) - TEXT_OFFSET, - transform.translation.z - 0.01, - ), - scale: Vec3::splat(scale), + visibility: Visibility::Hidden, ..default() }, - ..default() - }) - .insert(IconText(entity)); + IconTitleText, + )); + }); +} + +pub(crate) fn set_favorite_pin( + commands: &mut Commands, + images: &mut Assets, + parent_entity: Entity, + config: &Res, +) { + const PIN_ICON_SVG: &[u8] = include_bytes!("../../assets/icons/pin_stroke_rounded.svg"); + if let Some(image) = load_svg_pin_from_bytes(PIN_ICON_SVG) { + let handle = images.add(image); + commands.entity(parent_entity).with_children(|parent| { + let transform = Transform { + translation: Vec3::new(config.icon_size / 3.0, config.icon_size / 3.0, 0.1), + scale: Vec3::splat(0.4), + ..default() + }; + + parent + .spawn(SpriteBundle { + texture: handle, + transform, + ..Default::default() + }) + .insert(FavoritePin); + }); + } } fn load_svg_pin_from_bytes(svg_bytes: &[u8]) -> Option { @@ -113,30 +141,3 @@ fn load_svg_pin_from_bytes(svg_bytes: &[u8]) -> Option { TextureFormat::Rgba8Unorm, )) } - -pub(crate) fn set_favorite_pin( - commands: &mut Commands, - images: &mut Assets, - parent_entity: Entity, - config: &Res, -) { - const PIN_ICON_SVG: &[u8] = include_bytes!("../../assets/icons/pin_stroke_rounded.svg"); - if let Some(image) = load_svg_pin_from_bytes(PIN_ICON_SVG) { - let handle = images.add(image); - commands.entity(parent_entity).with_children(|parent| { - let transform = Transform { - translation: Vec3::new(config.icon_size / 3.0, config.icon_size / 3.0, 0.1), - scale: Vec3::splat(0.4), - ..default() - }; - - parent - .spawn(SpriteBundle { - texture: handle, - transform, - ..Default::default() - }) - .insert(FavoritePin); - }); - } -} diff --git a/src/config.rs b/src/config.rs index 341a4f7..336b44a 100644 --- a/src/config.rs +++ b/src/config.rs @@ -16,6 +16,7 @@ pub struct Config { pub scroll_speed: f32, pub visible_items: usize, pub tilt_y: f32, + pub font_size: f32, } impl Default for Config { @@ -31,6 +32,7 @@ impl Default for Config { scroll_speed: 15.0, visible_items: 8, tilt_y: 0.25, + font_size: 16.0, } } } diff --git a/src/main.rs b/src/main.rs index aefc69b..fb44268 100644 --- a/src/main.rs +++ b/src/main.rs @@ -31,6 +31,13 @@ use std::sync::{mpsc::channel, Arc, Mutex}; use crate::systems::*; use systems::animation::ScrollAnimationState; +#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)] +enum AppSystemSet { + Input, + Logic, + Animation, +} + fn main() { let config = load_config(); let client_list = load_clients(); @@ -58,7 +65,7 @@ fn main() { .add_plugins(SvgPlugin) .insert_resource(ClearColor(Color::NONE)) .insert_resource(ClientList(client_list)) - .insert_resource(IconPositions::default()) + .insert_resource(ShowTitles(false)) .insert_resource(UiState::default()) .insert_resource(favorites) @@ -73,30 +80,43 @@ fn main() { .add_systems(Startup, setup_hyprland_monitor) .add_systems(Update, cleanup_duplicate_cameras) + .configure_sets( + Update, + (AppSystemSet::Input, AppSystemSet::Logic, AppSystemSet::Animation).chain(), + ) .add_systems( Update, ( - scroll_system, - scroll_with_arrows, - hover_system, - hover_animation_system, - icon_scale_animation_system, - collect_icon_data.before(update_text_positions), - update_text_positions, - icon_click_system, - toggle_favorite_system.in_set(StateUpdate), - toggle_titles, - drag_register_click_system, - drag_check_system, - drag_update_system, - drag_end_system.in_set(StateUpdate), - reset_positions_system, - reorder_icons_system.in_set(ReorderIcons), - process_hyprland_events, - exit_on_esc_or_q, - keybind_launch_visible_icons, - ) - .chain(), + // Input Systems + ( + scroll_system, + scroll_with_arrows, + icon_click_system, + toggle_titles, + drag_register_click_system, + exit_on_esc_or_q, + keybind_launch_visible_icons, + process_hyprland_events, + ).in_set(AppSystemSet::Input), + + // Logic Systems + ( + drag_check_system, + drag_update_system, + + hover_system, + toggle_favorite_system, + drag_end_system, + reorder_icons_system, + reset_positions_system, + ).in_set(AppSystemSet::Logic), + + // Animation Systems + ( + hover_animation_system, + icon_scale_animation_system, + ).in_set(AppSystemSet::Animation), + ), ) .run(); } @@ -107,7 +127,7 @@ fn setup( mut images: ResMut>, client_list: Res, windows: Query<&Window, With>, - show_titles: Res, + _show_titles: Res, favorites: Res, config: Res, ) { @@ -195,6 +215,8 @@ fn setup( hover_exit_timer: None, }); + add_icon_text(&mut commands, icon_entity, class, &asset_server, &config); + if let Some(client) = client_opt { add_client_address(&mut commands, icon_entity, client.address.clone()); commands @@ -203,31 +225,11 @@ fn setup( commands .entity(icon_entity) .insert(ClientClass(client.class.clone())); - } else if *is_favorite { - let placeholder_address = format!("pinned:{}", class); - add_client_address(&mut commands, icon_entity, placeholder_address.clone()); - commands - .entity(icon_entity) - .insert(ClientAddress(placeholder_address)); - commands - .entity(icon_entity) - .insert(ClientClass(class.clone())); } if *is_favorite { add_favorite(&mut commands, icon_entity, &mut images, &config); } - if show_titles.0 { - add_icon_text( - &mut commands, - icon_entity, - class, - transform, - scale, - &asset_server, - &config, - ); - } } } @@ -582,8 +584,6 @@ fn process_new_windows( commands, icon_entity, &client.class, - transform, - scale, asset_server, config, ); @@ -787,15 +787,7 @@ fn handle_hypr_open_window( .insert(ClientClass(client.class.clone())); if show_titles.0 { - add_icon_text( - commands, - icon_entity, - &client.class, - transform, - scale, - asset_server, - config, - ); + add_icon_text(commands, icon_entity, &class, asset_server, config); } } diff --git a/src/systems/icon.rs b/src/systems/icon.rs index 0d4af15..88b3788 100644 --- a/src/systems/icon.rs +++ b/src/systems/icon.rs @@ -1,38 +1,11 @@ use crate::types::*; use crate::config::Config; use crate::utils::calculate_icon_transform; -use crate::IconText; + use bevy::prelude::*; use bevy::window::PrimaryWindow; -pub fn collect_icon_data( - query: Query<(Entity, &Transform, &HoverTarget)>, - mut icon_positions: ResMut, -) { - icon_positions.0.clear(); - for (entity, transform, _hover) in query.iter() { - icon_positions - .0 - .insert(entity, (transform.translation, transform.scale)); - } -} -pub fn update_text_positions( - icon_query: Query<(&Transform, &ClientClass), Without>, - mut text_query: Query<(&mut Transform, &IconText)>, - config: Res, -) { - for (mut text_transform, icon_text) in text_query.iter_mut() { - if let Ok((icon_transform, _)) = icon_query.get(icon_text.0) { - let scale = icon_transform.scale.y; - text_transform.translation.x = icon_transform.translation.x; - text_transform.translation.y = - icon_transform.translation.y - (config.icon_size * scale / 2.0) - 2.0; - text_transform.translation.z = icon_transform.translation.z - 0.01; - text_transform.scale = Vec3::splat(scale); - } - } -} pub fn reorder_icons_system( mut q_icons: Query<(Entity, &ClientAddress, &mut Transform, &mut HoverTarget)>, diff --git a/src/systems/title.rs b/src/systems/title.rs index 6221ad2..6af556d 100644 --- a/src/systems/title.rs +++ b/src/systems/title.rs @@ -1,36 +1,23 @@ use bevy::prelude::*; use crate::types::*; -use crate::components::add_icon_text; -use crate::config::Config; + pub fn toggle_titles( - mut commands: Commands, mut show_titles: ResMut, keyboard_input: Res>, - q_icons: Query<(Entity, &Transform, &ClientClass, &HoverTarget)>, - asset_server: Res, - config: Res, - q_texts: Query>, + mut q_texts: Query<&mut Visibility, With>, ) { if keyboard_input.just_pressed(KeyCode::T) { show_titles.0 = !show_titles.0; - if show_titles.0 { - for (entity, transform, class, _hover) in q_icons.iter() { - add_icon_text( - &mut commands, - entity, - &class.0, - *transform, - transform.scale.y, - &asset_server, - &config, - ); - } + let new_visibility = if show_titles.0 { + Visibility::Inherited } else { - for entity in q_texts.iter() { - commands.entity(entity).despawn_recursive(); - } + Visibility::Hidden + }; + + for mut visibility in q_texts.iter_mut() { + *visibility = new_visibility; } } } diff --git a/src/types.rs b/src/types.rs index afc17fb..2fe8c72 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,6 +1,6 @@ use bevy::prelude::*; use serde::Deserialize; -use std::collections::HashMap; + use std::sync::{Arc, Mutex, mpsc::Receiver}; #[derive(Deserialize, Debug, Clone)] @@ -48,10 +48,7 @@ pub struct HoverTarget { } #[derive(Component)] -pub struct IconText(pub Entity); - -#[derive(Resource, Default)] -pub struct IconPositions(pub HashMap); +pub struct IconTitleText; #[derive(Resource)] pub struct ReorderTrigger(pub bool);