1
0
Fork 0
mirror of https://github.com/Zedfrigg/ironbar.git synced 2025-07-01 18:51:04 +02:00

fix: able to insert duplicate keys into collection

This replaces the custom `Collection` implementation with `IndexMap` from the crate of the same name.

Fixes #28.
This commit is contained in:
Jake Stanger 2022-11-05 17:32:01 +00:00
parent 5ebc84c7b9
commit 3a83bd31ab
No known key found for this signature in database
GPG key ID: C51FC8F9CB0BEA61
8 changed files with 24 additions and 185 deletions

1
Cargo.lock generated
View file

@ -1146,6 +1146,7 @@ dependencies = [
"glib", "glib",
"gtk", "gtk",
"gtk-layer-shell", "gtk-layer-shell",
"indexmap",
"lazy_static", "lazy_static",
"libcorn", "libcorn",
"mpd_client", "mpd_client",

View file

@ -17,8 +17,6 @@ tracing-error = "0.2.0"
tracing-appender = "0.2.2" tracing-appender = "0.2.2"
strip-ansi-escapes = "0.1.1" strip-ansi-escapes = "0.1.1"
color-eyre = "0.6.2" color-eyre = "0.6.2"
futures-util = "0.3.21"
chrono = "0.4.19"
serde = { version = "1.0.141", features = ["derive"] } serde = { version = "1.0.141", features = ["derive"] }
serde_json = "1.0.82" serde_json = "1.0.82"
serde_yaml = "0.9.4" serde_yaml = "0.9.4"
@ -26,6 +24,9 @@ toml = "0.5.9"
libcorn = "0.4.0" libcorn = "0.4.0"
lazy_static = "1.4.0" lazy_static = "1.4.0"
async_once = "0.2.6" async_once = "0.2.6"
indexmap = "1.9.1"
futures-util = "0.3.21"
chrono = "0.4.19"
regex = { version = "1.6.0", default-features = false, features = ["std"] } regex = { version = "1.6.0", default-features = false, features = ["std"] }
stray = { version = "0.1.2" } stray = { version = "0.1.2" }
dirs = "4.0.0" dirs = "4.0.0"

View file

@ -1,161 +0,0 @@
use serde::Serialize;
use std::slice::{Iter, IterMut};
use std::vec;
/// An ordered map.
/// Internally this is just two vectors -
/// one for keys and one for values.
#[derive(Debug, Clone, Serialize)]
pub struct Collection<TKey, TData> {
keys: Vec<TKey>,
values: Vec<TData>,
}
impl<TKey: PartialEq, TData> Collection<TKey, TData> {
/// Creates a new empty collection.
pub const fn new() -> Self {
Self {
keys: vec![],
values: vec![],
}
}
/// Inserts a new key/value pair at the end of the collection.
pub fn insert(&mut self, key: TKey, value: TData) {
self.keys.push(key);
self.values.push(value);
assert_eq!(self.keys.len(), self.values.len());
}
/// Gets a reference of the value for the specified key
/// if it exists in the collection.
pub fn get(&self, key: &TKey) -> Option<&TData> {
let index = self.keys.iter().position(|k| k == key);
match index {
Some(index) => self.values.get(index),
None => None,
}
}
/// Gets a mutable reference for the value with the specified key
/// if it exists in the collection.
pub fn get_mut(&mut self, key: &TKey) -> Option<&mut TData> {
let index = self.keys.iter().position(|k| k == key);
match index {
Some(index) => self.values.get_mut(index),
None => None,
}
}
/// Checks if a value for the given key exists inside the collection
pub fn contains(&self, key: &TKey) -> bool {
self.keys.contains(key)
}
/// Removes the key/value from the collection
/// if it exists
/// and returns the removed value.
pub fn remove(&mut self, key: &TKey) -> Option<TData> {
assert_eq!(self.keys.len(), self.values.len());
let index = self.keys.iter().position(|k| k == key);
if let Some(index) = index {
self.keys.remove(index);
Some(self.values.remove(index))
} else {
None
}
}
/// Gets the length of the collection.
pub fn len(&self) -> usize {
self.keys.len()
}
/// Gets a reference to the first value in the collection.
pub fn first(&self) -> Option<&TData> {
self.values.first()
}
/// Gets the values as a slice.
pub fn as_slice(&self) -> &[TData] {
self.values.as_slice()
}
/// Checks whether the collection is empty.
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
/// Gets an iterator for the collection.
pub fn iter(&self) -> Iter<'_, TData> {
self.values.iter()
}
/// Gets a mutable iterator for the collection
pub fn iter_mut(&mut self) -> IterMut<'_, TData> {
self.values.iter_mut()
}
}
impl<TKey: PartialEq, TData> From<(TKey, TData)> for Collection<TKey, TData> {
fn from((key, value): (TKey, TData)) -> Self {
let mut collection = Self::new();
collection.insert(key, value);
collection
}
}
impl<TKey: PartialEq, TData> FromIterator<(TKey, TData)> for Collection<TKey, TData> {
fn from_iter<T: IntoIterator<Item = (TKey, TData)>>(iter: T) -> Self {
let mut collection = Self::new();
for (key, value) in iter {
collection.insert(key, value);
}
collection
}
}
impl<'a, TKey: PartialEq, TData> IntoIterator for &'a Collection<TKey, TData> {
type Item = &'a TData;
type IntoIter = CollectionIntoIterator<'a, TKey, TData>;
fn into_iter(self) -> Self::IntoIter {
CollectionIntoIterator {
collection: self,
index: 0,
}
}
}
pub struct CollectionIntoIterator<'a, TKey, TData> {
collection: &'a Collection<TKey, TData>,
index: usize,
}
impl<'a, TKey: PartialEq, TData> Iterator for CollectionIntoIterator<'a, TKey, TData> {
type Item = &'a TData;
fn next(&mut self) -> Option<Self::Item> {
let res = self.collection.values.get(self.index);
self.index += 1;
res
}
}
impl<TKey: PartialEq, TData> Default for Collection<TKey, TData> {
fn default() -> Self {
Self::new()
}
}
impl<TKey: PartialEq, TData> IntoIterator for Collection<TKey, TData> {
type Item = TData;
type IntoIter = vec::IntoIter<TData>;
fn into_iter(self) -> Self::IntoIter {
self.values.into_iter()
}
}

View file

@ -1,6 +1,5 @@
mod bar; mod bar;
mod bridge_channel; mod bridge_channel;
mod collection;
mod config; mod config;
mod icon; mod icon;
mod logging; mod logging;

View file

@ -47,10 +47,10 @@ impl Module<gtk::Box> for FocusedModule {
.expect("Failed to get read lock on toplevels") .expect("Failed to get read lock on toplevels")
.clone(); .clone();
toplevels.into_iter().find(|(top, _)| top.active) toplevels.into_iter().find(|(_, (top, _))| top.active)
}); });
if let Some((top, _)) = focused { if let Some((_, (top, _))) = focused {
tx.try_send(ModuleUpdateEvent::Update((top.title.clone(), top.app_id)))?; tx.try_send(ModuleUpdateEvent::Update((top.title.clone(), top.app_id)))?;
} }

View file

@ -1,5 +1,4 @@
use super::open_state::OpenState; use super::open_state::OpenState;
use crate::collection::Collection;
use crate::icon::get_icon; use crate::icon::get_icon;
use crate::modules::launcher::{ItemEvent, LauncherUpdate}; use crate::modules::launcher::{ItemEvent, LauncherUpdate};
use crate::modules::ModuleUpdateEvent; use crate::modules::ModuleUpdateEvent;
@ -7,6 +6,7 @@ use crate::popup::Popup;
use crate::wayland::ToplevelInfo; use crate::wayland::ToplevelInfo;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Button, IconTheme, Image, Orientation}; use gtk::{Button, IconTheme, Image, Orientation};
use indexmap::IndexMap;
use std::rc::Rc; use std::rc::Rc;
use std::sync::RwLock; use std::sync::RwLock;
use tokio::sync::mpsc::Sender; use tokio::sync::mpsc::Sender;
@ -16,17 +16,17 @@ pub struct Item {
pub app_id: String, pub app_id: String,
pub favorite: bool, pub favorite: bool,
pub open_state: OpenState, pub open_state: OpenState,
pub windows: Collection<usize, Window>, pub windows: IndexMap<usize, Window>,
pub name: String, pub name: String,
} }
impl Item { impl Item {
pub const fn new(app_id: String, open_state: OpenState, favorite: bool) -> Self { pub fn new(app_id: String, open_state: OpenState, favorite: bool) -> Self {
Self { Self {
app_id, app_id,
favorite, favorite,
open_state, open_state,
windows: Collection::new(), windows: IndexMap::new(),
name: String::new(), name: String::new(),
} }
} }
@ -78,7 +78,7 @@ impl Item {
&self &self
.windows .windows
.iter() .iter()
.map(|win| &win.open_state) .map(|(_, win)| &win.open_state)
.collect::<Vec<_>>(), .collect::<Vec<_>>(),
); );
self.open_state = new_state; self.open_state = new_state;
@ -91,7 +91,7 @@ impl From<ToplevelInfo> for Item {
let name = toplevel.title.clone(); let name = toplevel.title.clone();
let app_id = toplevel.app_id.clone(); let app_id = toplevel.app_id.clone();
let mut windows = Collection::new(); let mut windows = IndexMap::new();
windows.insert(toplevel.id, toplevel.into()); windows.insert(toplevel.id, toplevel.into());
Self { Self {

View file

@ -3,7 +3,6 @@ mod open_state;
use self::item::{Item, ItemButton, Window}; use self::item::{Item, ItemButton, Window};
use self::open_state::OpenState; use self::open_state::OpenState;
use crate::collection::Collection;
use crate::icon::find_desktop_file; use crate::icon::find_desktop_file;
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext}; use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
use crate::wayland; use crate::wayland;
@ -12,6 +11,7 @@ use color_eyre::{Help, Report};
use glib::Continue; use glib::Continue;
use gtk::prelude::*; use gtk::prelude::*;
use gtk::{Button, IconTheme, Orientation}; use gtk::{Button, IconTheme, Orientation};
use indexmap::IndexMap;
use serde::Deserialize; use serde::Deserialize;
use std::process::{Command, Stdio}; use std::process::{Command, Stdio};
use std::sync::{Arc, Mutex}; use std::sync::{Arc, Mutex};
@ -90,8 +90,8 @@ impl Module<gtk::Box> for LauncherModule {
Item::new(app_id.to_string(), OpenState::Closed, true), Item::new(app_id.to_string(), OpenState::Closed, true),
) )
}) })
.collect::<Collection<_, _>>(), .collect::<IndexMap<_, _>>(),
None => Collection::new(), None => IndexMap::new(),
}; };
let items = Arc::new(Mutex::new(items)); let items = Arc::new(Mutex::new(items));
@ -108,7 +108,7 @@ impl Module<gtk::Box> for LauncherModule {
let mut items = items.lock().expect("Failed to get lock on items"); let mut items = items.lock().expect("Failed to get lock on items");
for (window, _) in open_windows.clone() { for (_, (window, _)) in open_windows.clone() {
let item = items.get_mut(&window.app_id); let item = items.get_mut(&window.app_id);
match item { match item {
Some(item) => { Some(item) => {
@ -121,7 +121,7 @@ impl Module<gtk::Box> for LauncherModule {
} }
let items = items.iter(); let items = items.iter();
for item in items { for (_, item) in items {
tx.try_send(ModuleUpdateEvent::Update(LauncherUpdate::AddItem( tx.try_send(ModuleUpdateEvent::Update(LauncherUpdate::AddItem(
item.clone(), item.clone(),
)))?; )))?;
@ -282,7 +282,7 @@ impl Module<gtk::Box> for LauncherModule {
let id = match event { let id = match event {
ItemEvent::FocusItem(app_id) => items ItemEvent::FocusItem(app_id) => items
.get(&app_id) .get(&app_id)
.and_then(|item| item.windows.first().map(|win| win.id)), .and_then(|item| item.windows.first().map(|(_, win)| win.id)),
ItemEvent::FocusWindow(id) => Some(id), ItemEvent::FocusWindow(id) => Some(id),
ItemEvent::OpenItem(_) => unreachable!(), ItemEvent::OpenItem(_) => unreachable!(),
}; };
@ -325,7 +325,7 @@ impl Module<gtk::Box> for LauncherModule {
let show_icons = self.show_icons; let show_icons = self.show_icons;
let orientation = info.bar_position.get_orientation(); let orientation = info.bar_position.get_orientation();
let mut buttons = Collection::<String, ItemButton>::new(); let mut buttons = IndexMap::<String, ItemButton>::new();
let controller_tx2 = context.controller_tx.clone(); let controller_tx2 = context.controller_tx.clone();
context.widget_rx.attach(None, move |event| { context.widget_rx.attach(None, move |event| {
@ -431,7 +431,7 @@ impl Module<gtk::Box> for LauncherModule {
placeholder.set_width_request(MAX_WIDTH); placeholder.set_width_request(MAX_WIDTH);
container.add(&placeholder); container.add(&placeholder);
let mut buttons = Collection::<String, Collection<usize, Button>>::new(); let mut buttons = IndexMap::<String, IndexMap<usize, Button>>::new();
{ {
let container = container.clone(); let container = container.clone();
@ -443,7 +443,7 @@ impl Module<gtk::Box> for LauncherModule {
let window_buttons = item let window_buttons = item
.windows .windows
.into_iter() .into_iter()
.map(|win| { .map(|(_, win)| {
let button = Button::builder() let button = Button::builder()
.label(&clamp(&win.name)) .label(&clamp(&win.name))
.height_request(40) .height_request(40)
@ -509,7 +509,7 @@ impl Module<gtk::Box> for LauncherModule {
// add app's buttons // add app's buttons
if let Some(buttons) = buttons.get(&app_id) { if let Some(buttons) = buttons.get(&app_id) {
for button in buttons { for (_, button) in buttons {
button.style_context().add_class("popup-item"); button.style_context().add_class("popup-item");
container.add(button); container.add(button);
} }

View file

@ -1,5 +1,4 @@
use super::{Env, ToplevelHandler}; use super::{Env, ToplevelHandler};
use crate::collection::Collection;
use crate::wayland::toplevel::{ToplevelEvent, ToplevelInfo}; use crate::wayland::toplevel::{ToplevelEvent, ToplevelInfo};
use crate::wayland::toplevel_manager::listen_for_toplevels; use crate::wayland::toplevel_manager::listen_for_toplevels;
use crate::wayland::ToplevelChange; use crate::wayland::ToplevelChange;
@ -21,7 +20,7 @@ use wayland_protocols::wlr::unstable::foreign_toplevel::v1::client::{
pub struct WaylandClient { pub struct WaylandClient {
pub outputs: Vec<OutputInfo>, pub outputs: Vec<OutputInfo>,
pub seats: Vec<WlSeat>, pub seats: Vec<WlSeat>,
pub toplevels: Arc<RwLock<Collection<usize, (ToplevelInfo, ZwlrForeignToplevelHandleV1)>>>, pub toplevels: Arc<RwLock<IndexMap<usize, (ToplevelInfo, ZwlrForeignToplevelHandleV1)>>>,
toplevel_tx: broadcast::Sender<ToplevelEvent>, toplevel_tx: broadcast::Sender<ToplevelEvent>,
_toplevel_rx: broadcast::Receiver<ToplevelEvent>, _toplevel_rx: broadcast::Receiver<ToplevelEvent>,
} }
@ -35,7 +34,7 @@ impl WaylandClient {
let toplevel_tx2 = toplevel_tx.clone(); let toplevel_tx2 = toplevel_tx.clone();
let toplevels = Arc::new(RwLock::new(Collection::new())); let toplevels = Arc::new(RwLock::new(IndexMap::new()));
let toplevels2 = toplevels.clone(); let toplevels2 = toplevels.clone();
// `queue` is not send so we need to handle everything inside the task // `queue` is not send so we need to handle everything inside the task