← luna

Commit 1267824

3
files
+127
added
-43
deleted
@@ -15,7 +15,7 @@ use libluna_render::{Scene, SoftwareRenderer, TextRun};
15 15 use crate::drm::{DrmBackend, DrmError};
16 16 use crate::input::{self, Events};
17 17 use crate::monitors::Outputs;
18 -use crate::state::{CursorModel, OverviewModel, Window, WindowModel, WorkspaceModel};
18 +use crate::state::{CursorModel, Overview, OverviewModel, Window, WindowModel, WorkspaceModel};
19 19 use crate::sync::LockExt;
20 20
21 21 /// Uruchamia wątek wyjścia (nested Wayland) o zadanym rozmiarze.
@@ -310,7 +310,7 @@ fn cursor_image() -> (Vec<u8>, u32, u32) {
310 310 fn scene_signature(
311 311 model: &WindowModel,
312 312 current_ws: usize,
313 - overview: bool,
313 + overview: Overview,
314 314 cursor: Option<Point>,
315 315 ) -> u64 {
316 316 let mut h: u64 = 0xcbf2_9ce4_8422_2325;
@@ -319,7 +319,12 @@ fn scene_signature(
319 319 h = h.wrapping_mul(0x0000_0100_0000_01b3);
320 320 };
321 321 mix(current_ws as u64);
322 - mix(u64::from(overview));
322 + mix(u64::from(overview.on));
323 + if let Some((slot, (px, py))) = overview.drag {
324 + mix(slot as u64);
325 + mix(px.to_bits());
326 + mix(py.to_bits());
327 + }
323 328 {
324 329 let windows = model.lock_or_recover();
325 330 mix(windows.len() as u64);
@@ -350,7 +355,7 @@ fn build_scene(
350 355 model: &WindowModel,
351 356 current_ws: usize,
352 357 ws_count: usize,
353 - overview: bool,
358 + overview: Overview,
354 359 _is_primary: bool,
355 360 cursor: Option<Point>,
356 361 ) {
@@ -432,8 +437,17 @@ fn build_scene(
432 437 }
433 438
434 439 // Podgląd okien i przestrzeni (Super+Tab) — na każdym monitorze (jego okna).
435 - if overview {
436 - draw_overview(scene, size, ox, oy, model, current_ws, ws_count);
440 + if overview.on {
441 + draw_overview(
442 + scene,
443 + size,
444 + ox,
445 + oy,
446 + model,
447 + current_ws,
448 + ws_count,
449 + overview.drag,
450 + );
437 451 }
438 452
439 453 // Kursor rysuje ten monitor, na którym leży (tryb DRM — nikt go za nas nie narysuje).
@@ -460,14 +474,16 @@ fn fill_vgradient(scene: &mut Scene, rect: Rect, top: Color, bottom: Color) {
460 474 }
461 475
462 476 /// Rysuje podgląd okien i przestrzeni — kafelki z podglądem treści + pasek przestrzeni.
477 +#[allow(clippy::too_many_arguments)]
463 478 fn draw_overview(
464 479 scene: &mut Scene,
465 480 size: Size,
466 481 ox: f64,
467 - _oy: f64,
482 + oy: f64,
468 483 model: &WindowModel,
469 484 current_ws: usize,
470 485 ws_count: usize,
486 + drag: Option<(usize, (f64, f64))>,
471 487 ) {
472 488 // Przygaszenie tła.
473 489 scene.fill_rect(
@@ -519,6 +535,19 @@ fn draw_overview(
519 535 .with_color(Color::from_rgba8(0xf5, 0xf5, 0xf5, 0xff)),
520 536 );
521 537 }
538 + // „Duch” przeciąganego okna pod kursorem.
539 + if let Some((slot, (px, py))) = drag {
540 + let local = Point::new(px - ox, py - oy);
541 + let g = Rect::from_xywh(local.x - 90.0, local.y - 60.0, 180.0, 120.0);
542 + scene.fill_rounded_rect(g, 8.0, Color::from_rgba8(0x5b, 0x8d, 0xef, 0xcc));
543 + scene.stroke_rounded_rect(g, 8.0, 2.0, Color::from_rgba8(0xff, 0xff, 0xff, 0xdd));
544 + if let Some(w) = windows.get(slot) {
545 + scene.text(
546 + TextRun::new(Point::new(g.x() + 10.0, g.y() + 20.0), &w.title, 13.0)
547 + .with_color(Color::from_rgba8(0xff, 0xff, 0xff, 0xff)),
548 + );
549 + }
550 + }
522 551 }
523 552
524 553 /// Klasyczna strzałka kursora (biała z ciemnym obrysem).
@@ -578,7 +607,7 @@ mod tests {
578 607 &model,
579 608 0,
580 609 1,
581 - false,
610 + Overview::default(),
582 611 true,
583 612 Some(Point::new(100.0, 100.0)),
584 613 );
@@ -633,7 +662,7 @@ mod tests {
633 662 &model,
634 663 0,
635 664 1,
636 - false,
665 + Overview::default(),
637 666 true,
638 667 Some(Point::new(100.0, 100.0)),
639 668 );
@@ -292,6 +292,11 @@ fn content_local(state: &State, slot: usize, pos: Point) -> (f64, f64) {
292 292
293 293 fn pointer_motion(state: &mut State, handle: &DisplayHandle, pos: Point) {
294 294 state.pointer_pos = (pos.x, pos.y);
295 + if state.overview_on() {
296 + // W podglądzie ruch myszy tylko aktualizuje „ducha” przeciąganego okna.
297 + state.move_overview_drag((pos.x, pos.y));
298 + return;
299 + }
295 300 if state.resize.is_some() {
296 301 do_resize(state, pos);
297 302 return;
@@ -329,10 +334,34 @@ fn pointer_button(
329 334 button: PointerButton,
330 335 st: ButtonState,
331 336 ) {
332 - // W trybie podglądu klik wybiera kafelek/chip lub zamyka podgląd.
337 + // W trybie podglądu: przeciąganie kafelka, wybór przestrzeni albo zamknięcie.
333 338 if state.overview_on() {
334 - if st == ButtonState::Pressed && button == PointerButton::Left {
335 - overview_click(state, pos);
339 + if button == PointerButton::Left {
340 + match st {
341 + ButtonState::Pressed => {
342 + if let Some(slot) = overview_tile_at(state, pos) {
343 + state.set_overview_drag(Some((slot, (pos.x, pos.y))));
344 + } else if let Some(i) = overview_chip_at(state, pos) {
345 + state.set_overview(false);
346 + switch_workspace(state, i);
347 + } else {
348 + state.set_overview(false);
349 + }
350 + }
351 + ButtonState::Released => {
352 + let drag = state.overview.lock_or_recover().drag;
353 + if let Some((slot, _)) = drag {
354 + if let Some(i) = overview_chip_at(state, pos) {
355 + // Upuszczenie na przestrzeni — przenieś okno.
356 + move_window_to_workspace(state, slot, i);
357 + } else {
358 + // Zwykły klik — aktywuj okno.
359 + raise_and_focus(state, slot);
360 + }
361 + state.set_overview(false);
362 + }
363 + }
364 + }
336 365 }
337 366 return;
338 367 }
@@ -983,17 +1012,19 @@ fn move_window_to_workspace(state: &mut State, slot: usize, ws: usize) {
983 1012 crate::workspace::broadcast_workspace_states(state);
984 1013 }
985 1014
986 -/// Klik w trybie podglądu: kafelek → fokus okna, chip → przestrzeń, poza → zamknij.
987 -fn overview_click(state: &mut State, pos: Point) {
988 - // Znajdź monitor pod kursorem i lokalny układ podglądu na nim.
989 - let (ox, oy, size) = {
990 - let outs = state.outputs.lock_or_recover();
991 - let idx = crate::monitors::output_at(&outs, pos.x, pos.y);
992 - match outs.get(idx) {
993 - Some(o) => (o.x as f64, o.y as f64, (o.width as f64, o.height as f64)),
994 - None => (0.0, 0.0, (1280.0, 800.0)),
995 - }
996 - };
1015 +/// Monitor pod punktem: `(origin_x, origin_y, (szerokość, wysokość))`.
1016 +fn overview_output(state: &State, pos: Point) -> (f64, f64, (f64, f64)) {
1017 + let outs = state.outputs.lock_or_recover();
1018 + let idx = crate::monitors::output_at(&outs, pos.x, pos.y);
1019 + match outs.get(idx) {
1020 + Some(o) => (o.x as f64, o.y as f64, (o.width as f64, o.height as f64)),
1021 + None => (0.0, 0.0, (1280.0, 800.0)),
1022 + }
1023 +}
1024 +
1025 +/// Slot okna pod punktem w podglądzie (na monitorze pod kursorem).
1026 +fn overview_tile_at(state: &State, pos: Point) -> Option<usize> {
1027 + let (ox, oy, size) = overview_output(state, pos);
997 1028 let local = Point::new(pos.x - ox, pos.y - oy);
998 1029 let cur_ws = *state.ws_current.lock_or_recover();
999 1030 let slots = {
@@ -1001,21 +1032,19 @@ fn overview_click(state: &mut State, pos: Point) {
1001 1032 crate::state::overview_slots_on(&w, cur_ws, ox, size.0)
1002 1033 };
1003 1034 let tiles = crate::state::overview_tiles(slots.len(), size);
1004 - if let Some(k) = tiles.iter().position(|r| r.contains(local)) {
1005 - if let Some(&slot) = slots.get(k) {
1006 - state.set_overview(false);
1007 - raise_and_focus(state, slot);
1008 - return;
1009 - }
1010 - }
1011 - let chips = crate::state::overview_ws_chips(state.ws_count, size);
1012 - if let Some(i) = chips.iter().position(|r| r.contains(local)) {
1013 - state.set_overview(false);
1014 - switch_workspace(state, i);
1015 - return;
1016 - }
1017 - // Klik poza kafelkami — zamknij podgląd.
1018 - state.set_overview(false);
1035 + tiles
1036 + .iter()
1037 + .position(|r| r.contains(local))
1038 + .and_then(|k| slots.get(k).copied())
1039 +}
1040 +
1041 +/// Indeks „chipa” przestrzeni pod punktem.
1042 +fn overview_chip_at(state: &State, pos: Point) -> Option<usize> {
1043 + let (ox, oy, size) = overview_output(state, pos);
1044 + let local = Point::new(pos.x - ox, pos.y - oy);
1045 + crate::state::overview_ws_chips(state.ws_count, size)
1046 + .iter()
1047 + .position(|r| r.contains(local))
1019 1048 }
1020 1049
1021 1050 /// Wysyła wciśnięcia/zwolnienia klawiszy modyfikujących wynikające z bieżącego stanu.
@@ -174,8 +174,17 @@ pub type CursorModel = Arc<Mutex<Option<Point>>>;
174 174 /// Współdzielony numer bieżącej przestrzeni roboczej (kompozytor ↔ wątek wyjścia).
175 175 pub type WorkspaceModel = Arc<Mutex<usize>>;
176 176
177 +/// Stan podglądu okien/przestrzeni (współdzielony z wątkiem wyjścia).
178 +#[derive(Debug, Clone, Copy, Default)]
179 +pub struct Overview {
180 + /// Czy podgląd jest włączony.
181 + pub on: bool,
182 + /// Przeciągane okno: `(slot, pozycja kursora)`.
183 + pub drag: Option<(usize, (f64, f64))>,
184 +}
185 +
177 186 /// Współdzielony tryb podglądu okien/przestrzeni (kompozytor ↔ wątek wyjścia).
178 -pub type OverviewModel = Arc<Mutex<bool>>;
187 +pub type OverviewModel = Arc<Mutex<Overview>>;
179 188
180 189 /// Margines podglądu (od krawędzi ekranu).
181 190 pub const OVERVIEW_PAD: f64 = 56.0;
@@ -358,7 +367,7 @@ impl Default for State {
358 367 toplevel_managers: Vec::new(),
359 368 ws_count: 4,
360 369 ws_current: Arc::new(Mutex::new(0)),
361 - overview: Arc::new(Mutex::new(false)),
370 + overview: Arc::new(Mutex::new(Overview::default())),
362 371 ws_managers: Vec::new(),
363 372 ws_groups: Vec::new(),
364 373 ws_handles: Vec::new(),
@@ -397,12 +406,29 @@ impl State {
397 406
398 407 /// Czy trwa podgląd okien/przestrzeni.
399 408 pub fn overview_on(&self) -> bool {
400 - *self.overview.lock_or_recover()
409 + self.overview.lock_or_recover().on
401 410 }
402 411
403 - /// Włącza/wyłącza podgląd okien/przestrzeni.
412 + /// Włącza/wyłącza podgląd okien/przestrzeni (wyłączenie kończy przeciąganie).
404 413 pub fn set_overview(&self, on: bool) {
405 - *self.overview.lock_or_recover() = on;
414 + let mut o = self.overview.lock_or_recover();
415 + o.on = on;
416 + if !on {
417 + o.drag = None;
418 + }
419 + }
420 +
421 + /// Ustawia przeciągane okno podglądu: `(slot, pozycja kursora)`.
422 + pub fn set_overview_drag(&self, drag: Option<(usize, (f64, f64))>) {
423 + self.overview.lock_or_recover().drag = drag;
424 + }
425 +
426 + /// Aktualizuje tylko pozycję przeciąganego okna (zachowując slot).
427 + pub fn move_overview_drag(&self, pos: (f64, f64)) {
428 + let mut o = self.overview.lock_or_recover();
429 + if let Some((slot, _)) = o.drag {
430 + o.drag = Some((slot, pos));
431 + }
406 432 }
407 433 }
408 434