mirror of
https://github.com/JakeStanger/ironbar.git
synced 2024-10-04 02:08:25 +03: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
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -1146,6 +1146,7 @@ dependencies = [
|
||||
"glib",
|
||||
"gtk",
|
||||
"gtk-layer-shell",
|
||||
"indexmap",
|
||||
"lazy_static",
|
||||
"libcorn",
|
||||
"mpd_client",
|
||||
|
@ -17,8 +17,6 @@ tracing-error = "0.2.0"
|
||||
tracing-appender = "0.2.2"
|
||||
strip-ansi-escapes = "0.1.1"
|
||||
color-eyre = "0.6.2"
|
||||
futures-util = "0.3.21"
|
||||
chrono = "0.4.19"
|
||||
serde = { version = "1.0.141", features = ["derive"] }
|
||||
serde_json = "1.0.82"
|
||||
serde_yaml = "0.9.4"
|
||||
@ -26,6 +24,9 @@ toml = "0.5.9"
|
||||
libcorn = "0.4.0"
|
||||
lazy_static = "1.4.0"
|
||||
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"] }
|
||||
stray = { version = "0.1.2" }
|
||||
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 bridge_channel;
|
||||
mod collection;
|
||||
mod config;
|
||||
mod icon;
|
||||
mod logging;
|
||||
|
@ -47,10 +47,10 @@ impl Module<gtk::Box> for FocusedModule {
|
||||
.expect("Failed to get read lock on toplevels")
|
||||
.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)))?;
|
||||
}
|
||||
|
||||
|
@ -1,5 +1,4 @@
|
||||
use super::open_state::OpenState;
|
||||
use crate::collection::Collection;
|
||||
use crate::icon::get_icon;
|
||||
use crate::modules::launcher::{ItemEvent, LauncherUpdate};
|
||||
use crate::modules::ModuleUpdateEvent;
|
||||
@ -7,6 +6,7 @@ use crate::popup::Popup;
|
||||
use crate::wayland::ToplevelInfo;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{Button, IconTheme, Image, Orientation};
|
||||
use indexmap::IndexMap;
|
||||
use std::rc::Rc;
|
||||
use std::sync::RwLock;
|
||||
use tokio::sync::mpsc::Sender;
|
||||
@ -16,17 +16,17 @@ pub struct Item {
|
||||
pub app_id: String,
|
||||
pub favorite: bool,
|
||||
pub open_state: OpenState,
|
||||
pub windows: Collection<usize, Window>,
|
||||
pub windows: IndexMap<usize, Window>,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
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 {
|
||||
app_id,
|
||||
favorite,
|
||||
open_state,
|
||||
windows: Collection::new(),
|
||||
windows: IndexMap::new(),
|
||||
name: String::new(),
|
||||
}
|
||||
}
|
||||
@ -78,7 +78,7 @@ impl Item {
|
||||
&self
|
||||
.windows
|
||||
.iter()
|
||||
.map(|win| &win.open_state)
|
||||
.map(|(_, win)| &win.open_state)
|
||||
.collect::<Vec<_>>(),
|
||||
);
|
||||
self.open_state = new_state;
|
||||
@ -91,7 +91,7 @@ impl From<ToplevelInfo> for Item {
|
||||
let name = toplevel.title.clone();
|
||||
let app_id = toplevel.app_id.clone();
|
||||
|
||||
let mut windows = Collection::new();
|
||||
let mut windows = IndexMap::new();
|
||||
windows.insert(toplevel.id, toplevel.into());
|
||||
|
||||
Self {
|
||||
|
@ -3,7 +3,6 @@ mod open_state;
|
||||
|
||||
use self::item::{Item, ItemButton, Window};
|
||||
use self::open_state::OpenState;
|
||||
use crate::collection::Collection;
|
||||
use crate::icon::find_desktop_file;
|
||||
use crate::modules::{Module, ModuleInfo, ModuleUpdateEvent, ModuleWidget, WidgetContext};
|
||||
use crate::wayland;
|
||||
@ -12,6 +11,7 @@ use color_eyre::{Help, Report};
|
||||
use glib::Continue;
|
||||
use gtk::prelude::*;
|
||||
use gtk::{Button, IconTheme, Orientation};
|
||||
use indexmap::IndexMap;
|
||||
use serde::Deserialize;
|
||||
use std::process::{Command, Stdio};
|
||||
use std::sync::{Arc, Mutex};
|
||||
@ -90,8 +90,8 @@ impl Module<gtk::Box> for LauncherModule {
|
||||
Item::new(app_id.to_string(), OpenState::Closed, true),
|
||||
)
|
||||
})
|
||||
.collect::<Collection<_, _>>(),
|
||||
None => Collection::new(),
|
||||
.collect::<IndexMap<_, _>>(),
|
||||
None => IndexMap::new(),
|
||||
};
|
||||
|
||||
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");
|
||||
|
||||
for (window, _) in open_windows.clone() {
|
||||
for (_, (window, _)) in open_windows.clone() {
|
||||
let item = items.get_mut(&window.app_id);
|
||||
match item {
|
||||
Some(item) => {
|
||||
@ -121,7 +121,7 @@ impl Module<gtk::Box> for LauncherModule {
|
||||
}
|
||||
|
||||
let items = items.iter();
|
||||
for item in items {
|
||||
for (_, item) in items {
|
||||
tx.try_send(ModuleUpdateEvent::Update(LauncherUpdate::AddItem(
|
||||
item.clone(),
|
||||
)))?;
|
||||
@ -282,7 +282,7 @@ impl Module<gtk::Box> for LauncherModule {
|
||||
let id = match event {
|
||||
ItemEvent::FocusItem(app_id) => items
|
||||
.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::OpenItem(_) => unreachable!(),
|
||||
};
|
||||
@ -325,7 +325,7 @@ impl Module<gtk::Box> for LauncherModule {
|
||||
let show_icons = self.show_icons;
|
||||
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();
|
||||
context.widget_rx.attach(None, move |event| {
|
||||
@ -431,7 +431,7 @@ impl Module<gtk::Box> for LauncherModule {
|
||||
placeholder.set_width_request(MAX_WIDTH);
|
||||
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();
|
||||
@ -443,7 +443,7 @@ impl Module<gtk::Box> for LauncherModule {
|
||||
let window_buttons = item
|
||||
.windows
|
||||
.into_iter()
|
||||
.map(|win| {
|
||||
.map(|(_, win)| {
|
||||
let button = Button::builder()
|
||||
.label(&clamp(&win.name))
|
||||
.height_request(40)
|
||||
@ -509,7 +509,7 @@ impl Module<gtk::Box> for LauncherModule {
|
||||
|
||||
// add app's buttons
|
||||
if let Some(buttons) = buttons.get(&app_id) {
|
||||
for button in buttons {
|
||||
for (_, button) in buttons {
|
||||
button.style_context().add_class("popup-item");
|
||||
container.add(button);
|
||||
}
|
||||
|
@ -1,5 +1,4 @@
|
||||
use super::{Env, ToplevelHandler};
|
||||
use crate::collection::Collection;
|
||||
use crate::wayland::toplevel::{ToplevelEvent, ToplevelInfo};
|
||||
use crate::wayland::toplevel_manager::listen_for_toplevels;
|
||||
use crate::wayland::ToplevelChange;
|
||||
@ -21,7 +20,7 @@ use wayland_protocols::wlr::unstable::foreign_toplevel::v1::client::{
|
||||
pub struct WaylandClient {
|
||||
pub outputs: Vec<OutputInfo>,
|
||||
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_rx: broadcast::Receiver<ToplevelEvent>,
|
||||
}
|
||||
@ -35,7 +34,7 @@ impl WaylandClient {
|
||||
|
||||
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();
|
||||
|
||||
// `queue` is not send so we need to handle everything inside the task
|
||||
|
Loading…
Reference in New Issue
Block a user