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:
parent
5ebc84c7b9
commit
3a83bd31ab
8 changed files with 24 additions and 185 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -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",
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)))?;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue