Skroll-stegning i fönsterlistan + klick/launch-debugloggar
All checks were successful
check / check (push) Successful in 1m34s

Med en grupps lista öppen stegar skroll (på gruppikonen eller över
listan) markeringen mellan raderna — markerad rad får accentbakgrund
och triggar samma förhandsvisning som hover (peek/fokus enligt
list_hover). Funkar i både hover- och klickläge; utan öppen lista
beter sig skroll som förut (cykla fönster). Watchdogen räknar
stegning som aktivitet så listan inte stängs mitt i.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-30 00:53:16 +02:00
parent 6be8c6fe27
commit 1c6ed7815b
5 changed files with 147 additions and 47 deletions

View File

@@ -18,6 +18,13 @@ use crate::settings;
use crate::toplevel::ToplevelInfo;
use crate::wayfire_ipc;
/// Handle till en öppen fönsterlista: gruppikonen skroll-stegar via denna.
pub struct GroupList {
pub key: String,
pub connector: String,
pub step: Rc<dyn Fn(f64)>,
}
/// Målet för den senast öppnade ikon-menyn (actions läser härifrån).
pub struct MenuTarget {
pub key: String,
@@ -343,7 +350,10 @@ pub fn popup_group_list(ctx: &Rc<Ctx>, item: &Rc<Item>, anchor: &gtk::Button) {
// fönster som tonats ner av peek — återställs vid radbyte/stängning
let dimmed: Rc<std::cell::RefCell<Vec<i64>>> = Rc::new(std::cell::RefCell::new(Vec::new()));
for w in &item.windows {
// bygg raderna först, koppla handlers efteråt (select behöver alla)
let windows: Rc<Vec<crate::toplevel::ToplevelInfo>> = Rc::new(item.windows.clone());
let mut rows_vec = Vec::new();
for w in windows.iter() {
let row = gtk::Button::new();
row.add_css_class("flat");
let title = if w.title.is_empty() { item.meta.display_name.clone() } else { w.title.clone() };
@@ -352,6 +362,68 @@ pub fn popup_group_list(ctx: &Rc<Ctx>, item: &Rc<Item>, anchor: &gtk::Button) {
label.set_max_width_chars(40);
label.set_halign(gtk::Align::Start);
row.set_child(Some(&label));
list.append(&row);
rows_vec.push(row);
}
let rows: Rc<Vec<gtk::Button>> = Rc::new(rows_vec);
// markera rad idx: visuell markering + förhandsvisning (peek/fokus)
let selected = Rc::new(std::cell::Cell::new(None::<usize>));
let select: Rc<dyn Fn(usize)> = Rc::new({
let rows = rows.clone();
let windows = windows.clone();
let dimmed = dimmed.clone();
let selected = selected.clone();
let list_hover = list_hover.clone();
let ctx2 = ctx.clone();
move |idx: usize| {
for (i, r) in rows.iter().enumerate() {
if i == idx {
r.add_css_class("selected");
} else {
r.remove_css_class("selected");
}
}
selected.set(Some(idx));
let w = &windows[idx];
match list_hover {
ListHover::Peek if peek_possible => {
peek_restore(&dimmed.borrow());
*dimmed.borrow_mut() = peek_apply(w);
}
ListHover::Focus => {
if let Some(s) = ctx2.service.borrow().as_ref() {
s.activate(w);
}
}
_ => {}
}
}
});
// skroll-stegning (från gruppikonen eller över listan)
let step: Rc<dyn Fn(f64)> = Rc::new({
let select = select.clone();
let selected = selected.clone();
let n = windows.len();
move |dy: f64| {
if n == 0 {
return;
}
let next = match selected.get() {
None => {
if dy > 0.0 { 0 } else { n - 1 }
}
Some(i) => {
if dy > 0.0 { (i + 1) % n } else { (i + n - 1) % n }
}
};
select(next);
}
});
// per rad: klick aktiverar; hover markerar (efter armering)
for (i, (row, w)) in rows.iter().zip(windows.iter()).enumerate() {
let ctx2 = ctx.clone();
let w2 = w.clone();
let pop2 = popover.clone();
@@ -364,47 +436,47 @@ pub fn popup_group_list(ctx: &Rc<Ctx>, item: &Rc<Item>, anchor: &gtk::Button) {
}
pop2.popdown();
});
// hover-effekt per rad: peek eller fokus
match list_hover {
ListHover::Peek if peek_possible => {
let mc = gtk::EventControllerMotion::new();
let w3 = w.clone();
let dimmed3 = dimmed.clone();
let armed3 = armed.clone();
mc.connect_enter(move |_, _, _| {
if !armed3.get() {
return;
}
peek_restore(&dimmed3.borrow());
*dimmed3.borrow_mut() = peek_apply(&w3);
});
let dimmed4 = dimmed.clone();
mc.connect_leave(move |_| {
peek_restore(&dimmed4.borrow());
dimmed4.borrow_mut().clear();
});
row.add_controller(mc);
}
ListHover::Focus => {
let mc = gtk::EventControllerMotion::new();
let ctx3 = ctx.clone();
let w3 = w.clone();
let armed3 = armed.clone();
mc.connect_enter(move |_, _, _| {
if !armed3.get() {
return;
}
if let Some(s) = ctx3.service.borrow().as_ref() {
s.activate(&w3);
}
});
row.add_controller(mc);
}
_ => {}
if list_hover != ListHover::None {
let mc = gtk::EventControllerMotion::new();
let select2 = select.clone();
let armed2 = armed.clone();
mc.connect_enter(move |_, _, _| {
if armed2.get() {
select2(i);
}
});
row.add_controller(mc);
}
list.append(&row);
}
// skroll över själva listan stegar också
{
let sc = gtk::EventControllerScroll::new(gtk::EventControllerScrollFlags::VERTICAL);
let step2 = step.clone();
sc.connect_scroll(move |_, _, dy| {
step2(dy);
glib::Propagation::Stop
});
popover.add_controller(sc);
}
// registrera handlet så gruppikonens skroll når stegningen
*ctx.group_list.borrow_mut() = Some(GroupList {
key: item.meta.key.clone(),
connector: connector.clone(),
step: step.clone(),
});
{
let ctx2 = ctx.clone();
let key = item.meta.key.clone();
popover.connect_closed(move |_| {
let mut gl = ctx2.group_list.borrow_mut();
if gl.as_ref().map(|h| h.key == key).unwrap_or(false) {
*gl = None;
}
});
}
popover.set_child(Some(&list));
popover.set_parent(anchor);
@@ -437,13 +509,19 @@ pub fn popup_group_list(ctx: &Rc<Ctx>, item: &Rc<Item>, anchor: &gtk::Button) {
}
popover.add_controller(mc.clone());
// watchdog: om pekaren aldrig ens når listan, stäng efter en stund
// watchdog: om pekaren aldrig når listan OCH ingen skroll-stegning
// sker, stäng efter en stund (upprepas; aktivitet = markerad rad)
{
let pop3 = popover.clone();
glib::timeout_add_local_once(std::time::Duration::from_millis(1500), move || {
if !mc.contains_pointer() {
pop3.popdown();
let selected2 = selected.clone();
let last_seen = std::cell::Cell::new(selected.get());
glib::timeout_add_local(std::time::Duration::from_millis(1500), move || {
if mc.contains_pointer() || selected2.get() != last_seen.get() {
last_seen.set(selected2.get());
return glib::ControlFlow::Continue;
}
pop3.popdown();
glib::ControlFlow::Break
});
}