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