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

@@ -47,6 +47,8 @@ pub struct Ctx {
pub refresh_pending: Cell<bool>,
/// den enda popover som får vara öppen åt gången
pub current_popover: RefCell<Option<gtk::Popover>>,
/// öppen fönsterlista (för skroll-stegning från gruppikonen)
pub group_list: RefCell<Option<menu::GroupList>>,
}
pub struct Dock {
@@ -730,6 +732,17 @@ fn build_item_widget(ctx: &Rc<Ctx>, connector: &str, item: Rc<Item>) -> gtk::But
let item = item.clone();
let conn2 = connector.to_string();
scroll.connect_scroll(move |_, _, dy| {
// om den här gruppens lista är öppen: stega markeringen i den
let step = ctx
.group_list
.borrow()
.as_ref()
.filter(|h| h.key == item.meta.key && h.connector == conn2)
.map(|h| h.step.clone());
if let Some(step) = step {
step(dy);
return glib::Propagation::Stop;
}
if ctx.cfg.borrow().for_output(&conn2).behavior.scroll == ScrollAction::CycleWindows {
let live = live_item(&ctx, &conn2, &item);
cycle_windows(&ctx, &live, dy > 0.0);
@@ -745,6 +758,12 @@ fn build_item_widget(ctx: &Rc<Ctx>, connector: &str, item: Rc<Item>) -> gtk::But
}
fn primary_action(ctx: &Rc<Ctx>, connector: &str, item: &Rc<Item>, anchor: &gtk::Button) {
debug!(
"vänsterklick: {} ({} fönster, pinnad={})",
item.meta.display_name,
item.windows.len(),
item.pinned
);
let service = ctx.service.borrow();
let Some(service) = service.as_ref() else {
icons::launch(&item.meta);

View File

@@ -88,8 +88,9 @@ pub fn lookup_pin(desktop_id: &str) -> Option<AppMeta> {
/// Starta en app (ny instans).
pub fn launch(meta: &AppMeta) {
if let Some(info) = &meta.app_info {
if let Err(e) = info.launch(&[], None::<&gio::AppLaunchContext>) {
log::warn!("kunde inte starta {}: {e}", meta.display_name);
match info.launch(&[], None::<&gio::AppLaunchContext>) {
Ok(_) => log::info!("startade {}", meta.display_name),
Err(e) => log::warn!("kunde inte starta {}: {e}", meta.display_name),
}
} else {
log::warn!("ingen .desktop för {} — kan inte starta", meta.display_name);

View File

@@ -158,6 +158,7 @@ fn activate(app: &gtk::Application, cfg_path: PathBuf) {
open_popovers: Cell::new(0),
refresh_pending: Cell::new(false),
current_popover: RefCell::new(None),
group_list: RefCell::new(None),
});
// CSS + rebuild/refresh-logik

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
});
}

View File

@@ -141,7 +141,8 @@ fn css_body(cfg: &Config, p: &str) -> String {
}}
{p}popover.waydock-menu modelbutton:hover,
{p}popover.waydock-menu button.flat:hover {{
{p}popover.waydock-menu button.flat:hover,
{p}popover.waydock-menu button.flat.selected {{
background-color: {accent_soft};
}}
"#,