Skip to content

Commit 2e73d09

Browse files
committed
improv(stack): create and borrow tab models directly in the view
1 parent 993918f commit 2e73d09

File tree

4 files changed

+96
-44
lines changed

4 files changed

+96
-44
lines changed

src/shell/element/stack.rs

Lines changed: 29 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ impl fmt::Debug for CosmicStack {
9393
#[derive(Debug, Clone)]
9494
pub struct CosmicStackInternal {
9595
windows: Arc<Mutex<Vec<CosmicSurface>>>,
96+
tab_models: Arc<[tab::Model]>,
9697
active: Arc<AtomicUsize>,
9798
activated: Arc<AtomicBool>,
9899
group_focused: Arc<AtomicBool>,
@@ -133,7 +134,11 @@ impl CosmicStack {
133134
handle: LoopHandle<'static, crate::state::State>,
134135
theme: cosmic::Theme,
135136
) -> CosmicStack {
136-
let windows = windows.map(Into::into).collect::<Vec<_>>();
137+
let (tab_models, windows) = windows
138+
.map(Into::into)
139+
.map(|window| (tab::Model::from(&window), window))
140+
.collect::<(Vec<_>, Vec<_>)>();
141+
137142
assert!(!windows.is_empty());
138143

139144
for window in &windows {
@@ -146,6 +151,7 @@ impl CosmicStack {
146151
CosmicStack(IcedElement::new(
147152
CosmicStackInternal {
148153
windows: Arc::new(Mutex::new(windows)),
154+
tab_models: Arc::from(tab_models),
149155
active: Arc::new(AtomicUsize::new(0)),
150156
activated: Arc::new(AtomicBool::new(false)),
151157
group_focused: Arc::new(AtomicBool::new(false)),
@@ -791,6 +797,7 @@ pub enum Message {
791797
PotentialTabDragStart(usize),
792798
Activate(usize),
793799
Close(usize),
800+
Refresh,
794801
ScrollForward,
795802
ScrollBack,
796803
Scrolled,
@@ -838,6 +845,15 @@ impl Program for CosmicStackInternal {
838845
last_seat: Option<&(Seat<State>, Serial)>,
839846
) -> Task<Self::Message> {
840847
match message {
848+
Message::Refresh => {
849+
self.tab_models = self
850+
.windows
851+
.lock()
852+
.unwrap()
853+
.iter()
854+
.map(tab::Model::from)
855+
.collect();
856+
}
841857
Message::DragStart => {
842858
if let Some((seat, serial)) = last_seat.cloned() {
843859
let active = self.active.load(Ordering::SeqCst);
@@ -997,12 +1013,14 @@ impl Program for CosmicStackInternal {
9971013
}
9981014

9991015
fn view(&self) -> CosmicElement<'_, Self::Message> {
1000-
let windows = self.windows.lock().unwrap();
10011016
if self.geometry.lock().unwrap().is_none() {
10021017
return iced_widget::row(Vec::new()).into();
10031018
};
1019+
10041020
let active = self.active.load(Ordering::SeqCst);
1021+
let activated = self.activated.load(Ordering::SeqCst);
10051022
let group_focused = self.group_focused.load(Ordering::SeqCst);
1023+
let maximized = self.windows.lock().unwrap()[active].is_maximized(false);
10061024

10071025
let elements = vec![
10081026
cosmic_widget::icon::from_name("window-stack-symbolic")
@@ -1029,20 +1047,14 @@ impl Program for CosmicStackInternal {
10291047
.into(),
10301048
CosmicElement::new(
10311049
Tabs::new(
1032-
windows.iter().enumerate().map(|(i, w)| {
1033-
let user_data = w.user_data();
1034-
user_data.insert_if_missing(Id::unique);
1035-
Tab::new(
1036-
w.title(),
1037-
w.app_id(),
1038-
user_data.get::<Id>().unwrap().clone(),
1039-
)
1040-
.on_press(Message::PotentialTabDragStart(i))
1041-
.on_right_click(Message::TabMenu(i))
1042-
.on_close(Message::Close(i))
1050+
self.tab_models.iter().enumerate().map(|(i, tab)| {
1051+
Tab::new(tab)
1052+
.on_press(Message::PotentialTabDragStart(i))
1053+
.on_right_click(Message::TabMenu(i))
1054+
.on_close(Message::Close(i))
10431055
}),
10441056
active,
1045-
windows[active].is_activated(false),
1057+
activated,
10461058
group_focused,
10471059
)
10481060
.id(SCROLLABLE_ID.clone())
@@ -1064,7 +1076,7 @@ impl Program for CosmicStackInternal {
10641076
.into(),
10651077
];
10661078

1067-
let radius = if windows[active].is_maximized(false) {
1079+
let radius = if maximized {
10681080
Radius::from(0.0)
10691081
} else {
10701082
Radius::from([8.0, 8.0, 0.0, 0.0])
@@ -1251,6 +1263,8 @@ impl SpaceElement for CosmicStack {
12511263
SpaceElement::refresh(w)
12521264
});
12531265
});
1266+
1267+
self.0.queue_message(Message::Refresh);
12541268
SpaceElement::refresh(&self.0);
12551269
}
12561270
}

src/shell/element/stack/tab.rs

Lines changed: 52 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1+
use std::hash::{Hash, Hasher};
2+
13
use cosmic::{
24
font::Font,
35
iced::{
46
widget::{self, container::draw_background, rule::FillMode},
5-
Background,
7+
Background, Padding,
68
},
79
iced_core::{
810
alignment, event,
@@ -17,10 +19,12 @@ use cosmic::{
1719
},
1820
iced_widget::scrollable::AbsoluteOffset,
1921
theme,
20-
widget::{icon::from_name, Icon},
22+
widget::icon,
2123
Apply,
2224
};
2325

26+
use crate::shell::CosmicSurface;
27+
2428
use super::tab_text::tab_text;
2529

2630
#[derive(Clone, Copy)]
@@ -115,14 +119,47 @@ pub trait TabMessage: Clone {
115119
fn scrolled() -> Self;
116120
}
117121

122+
#[derive(Debug, Clone)]
123+
pub struct Model {
124+
pub id: Id,
125+
pub app_icon: cosmic::widget::icon::Handle,
126+
pub title: String,
127+
pub title_hash: u64,
128+
}
129+
130+
impl Model {
131+
pub fn new(id: Id, appid: String, title: String) -> Self {
132+
// Pre-emptively cache the hash of each title for more efficient diffing.
133+
let mut hasher = std::collections::hash_map::DefaultHasher::new();
134+
title.hash(&mut hasher);
135+
136+
Self {
137+
id,
138+
app_icon: icon::from_name(appid).size(16).handle(),
139+
title,
140+
title_hash: hasher.finish(),
141+
}
142+
}
143+
}
144+
145+
impl From<&CosmicSurface> for Model {
146+
fn from(window: &CosmicSurface) -> Self {
147+
let user_data = window.user_data();
148+
user_data.insert_if_missing(Id::unique);
149+
Self::new(
150+
user_data.get::<Id>().unwrap().clone(),
151+
window.app_id(),
152+
window.title(),
153+
)
154+
}
155+
}
156+
118157
struct LocalState {
119158
hovered: bool,
120159
}
121160

122-
pub struct Tab<Message: TabMessage> {
123-
id: Id,
124-
app_icon: Icon,
125-
title: String,
161+
pub struct Tab<'a, Message: TabMessage> {
162+
model: &'a Model,
126163
font: Font,
127164
close_message: Option<Message>,
128165
press_message: Option<Message>,
@@ -132,12 +169,10 @@ pub struct Tab<Message: TabMessage> {
132169
active: bool,
133170
}
134171

135-
impl<Message: TabMessage + 'static> Tab<Message> {
136-
pub fn new(title: impl Into<String>, app_id: impl Into<String>, id: Id) -> Self {
172+
impl<'a, Message: TabMessage + 'static> Tab<'a, Message> {
173+
pub fn new(model: &'a Model) -> Self {
137174
Tab {
138-
id,
139-
app_icon: from_name(app_id.into()).size(16).icon(),
140-
title: title.into(),
175+
model,
141176
font: cosmic::font::default(),
142177
close_message: None,
143178
press_message: None,
@@ -183,8 +218,8 @@ impl<Message: TabMessage + 'static> Tab<Message> {
183218
self
184219
}
185220

186-
pub(super) fn internal<'a>(self, idx: usize) -> TabInternal<'a, Message> {
187-
let mut close_button = from_name("window-close-symbolic")
221+
pub(super) fn internal(self, idx: usize) -> TabInternal<'a, Message> {
222+
let mut close_button = icon::from_name("window-close-symbolic")
188223
.size(16)
189224
.prefer_svg(true)
190225
.icon()
@@ -197,14 +232,14 @@ impl<Message: TabMessage + 'static> Tab<Message> {
197232

198233
let items = vec![
199234
widget::vertical_rule(4).class(self.rule_theme).into(),
200-
self.app_icon
235+
cosmic::widget::icon(self.model.app_icon.clone())
201236
.clone()
202237
.apply(widget::container)
203238
.width(Length::Shrink)
204239
.padding([2, 4, 2, 8])
205240
.center_y(Length::Fill)
206241
.into(),
207-
tab_text(self.title, self.active)
242+
tab_text(&self.model, self.active)
208243
.font(self.font)
209244
.font_size(14.0)
210245
.height(Length::Fill)
@@ -220,7 +255,7 @@ impl<Message: TabMessage + 'static> Tab<Message> {
220255
];
221256

222257
TabInternal {
223-
id: self.id,
258+
id: self.model.id.clone(),
224259
idx,
225260
active: self.active,
226261
background: self.background_theme,
@@ -299,6 +334,7 @@ where
299334
.min_height(size.height)
300335
.width(size.width)
301336
.height(size.height);
337+
302338
cosmic::iced_core::layout::flex::resolve(
303339
cosmic::iced_core::layout::flex::Axis::Horizontal,
304340
renderer,

src/shell/element/stack/tab_text.rs

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,11 @@ use cosmic::{
1313
},
1414
};
1515

16+
use super::tab::Model;
17+
1618
/// Text in a stack tab with an overflow gradient.
17-
pub fn tab_text(text: String, selected: bool) -> TabText {
18-
TabText::new(text, selected)
19+
pub fn tab_text(model: &Model, selected: bool) -> TabText {
20+
TabText::new(model, selected)
1921
}
2022

2123
struct LocalState {
@@ -25,24 +27,24 @@ struct LocalState {
2527
}
2628

2729
/// Text in a stack tab with an overflow gradient.
28-
pub struct TabText {
29-
text: String,
30+
pub struct TabText<'a> {
31+
model: &'a Model,
3032
font: cosmic::font::Font,
3133
font_size: f32,
3234
selected: bool,
3335
height: Length,
3436
width: Length,
3537
}
3638

37-
impl TabText {
38-
pub fn new(text: String, selected: bool) -> Self {
39+
impl<'a> TabText<'a> {
40+
pub fn new(model: &'a Model, selected: bool) -> Self {
3941
TabText {
4042
width: Length::Shrink,
4143
height: Length::Shrink,
4244
font: cosmic::font::default(),
4345
font_size: 14.0,
4446
selected,
45-
text,
47+
model,
4648
}
4749
}
4850

@@ -70,14 +72,14 @@ impl TabText {
7072

7173
fn create_hash(&self) -> u64 {
7274
let mut hasher = std::collections::hash_map::DefaultHasher::new();
73-
self.text.hash(&mut hasher);
75+
self.model.title_hash.hash(&mut hasher);
7476
self.selected.hash(&mut hasher);
7577
hasher.finish()
7678
}
7779

7880
fn create_paragraph(&self) -> <cosmic::Renderer as TextRenderer>::Paragraph {
7981
<cosmic::Renderer as TextRenderer>::Paragraph::with_text(Text {
80-
content: &self.text,
82+
content: &self.model.title,
8183
size: cosmic::iced_core::Pixels(self.font_size),
8284
bounds: Size::INFINITY,
8385
font: self.font,
@@ -90,7 +92,7 @@ impl TabText {
9092
}
9193
}
9294

93-
impl<Message> Widget<Message, cosmic::Theme, cosmic::Renderer> for TabText {
95+
impl<'a, Message> Widget<Message, cosmic::Theme, cosmic::Renderer> for TabText<'a> {
9496
fn tag(&self) -> tree::Tag {
9597
tree::Tag::of::<LocalState>()
9698
}
@@ -204,8 +206,8 @@ impl<Message> Widget<Message, cosmic::Theme, cosmic::Renderer> for TabText {
204206
}
205207
}
206208

207-
impl<Message: 'static> From<TabText> for cosmic::Element<'_, Message> {
208-
fn from(value: TabText) -> Self {
209+
impl<'a, Message: 'static> From<TabText<'a>> for cosmic::Element<'a, Message> {
210+
fn from(value: TabText<'a>) -> Self {
209211
Self::new(value)
210212
}
211213
}

src/shell/element/stack/tabs.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ where
132132
Message: TabMessage + 'static,
133133
{
134134
pub fn new(
135-
tabs: impl ExactSizeIterator<Item = Tab<Message>>,
135+
tabs: impl ExactSizeIterator<Item = Tab<'a, Message>>,
136136
active: usize,
137137
activated: bool,
138138
group_focused: bool,

0 commit comments

Comments
 (0)