Alvin Unreal 2 månader sedan
förälder
incheckning
21775f10a7
1 ändrade filer med 94 tillägg och 72 borttagningar
  1. 94 72
      companion/src/app.rs

+ 94 - 72
companion/src/app.rs

@@ -18,16 +18,16 @@ use crate::state::{
 const DEFAULT_SIZE: f32 = 120.0;
 const GAP: f32 = 10.0;
 
-const SIZE_PRESETS: &[(&str, f32)] = &[
-    ("S  ·  80px", 80.0),
-    ("M  ·  120px", 120.0),
-    ("L  ·  160px", 160.0),
-    ("XL  ·  200px", 200.0),
-];
+const SIZE_PRESETS: &[(&str, f32)] = &[("S", 80.0), ("M", 120.0), ("L", 160.0), ("XL", 200.0)];
+
+const MENU_W: f32 = 76.0;
+const MENU_H: f32 = 58.0;
+const MENU_PAD: f32 = 2.0;
 
 const SIZE_KEY: &str = "companion_size";
 const MENU_OPEN_KEY: &str = "companion_menu_open";
 const MENU_POS_KEY: &str = "companion_menu_pos";
+const MENU_JUST_OPENED_KEY: &str = "companion_menu_just_opened";
 
 #[derive(Clone, Debug, PartialEq, Eq)]
 struct WindowGeometryKey {
@@ -512,6 +512,10 @@ impl eframe::App for CompanionApp {
         let n = agent_frames.len().max(1);
         let (cols, rows) = grid_dims(n);
         let [win_w, win_h] = window_size(self.size, cols, rows);
+        let menu_open = ctx.data(|d| {
+            d.get_temp::<bool>(egui::Id::new(MENU_OPEN_KEY))
+                .unwrap_or(false)
+        });
 
         let geometry = WindowGeometryKey {
             session_id: session.session_id.clone(),
@@ -556,10 +560,6 @@ impl eframe::App for CompanionApp {
             self.spawn_niri_fallback([win_w, win_h], saved_position);
         }
 
-        let menu_open = ctx.data(|d| {
-            d.get_temp::<bool>(egui::Id::new(MENU_OPEN_KEY))
-                .unwrap_or(false)
-        });
         if !menu_open && ctx.input(|i| i.pointer.primary_pressed()) {
             self.drag_project_key = Some(project_key.clone());
         }
@@ -588,6 +588,7 @@ impl eframe::App for CompanionApp {
             ctx.data_mut(|d| {
                 d.insert_temp(egui::Id::new(MENU_POS_KEY), [cursor.x, cursor.y]);
                 d.insert_temp(egui::Id::new(MENU_OPEN_KEY), true);
+                d.insert_temp(egui::Id::new(MENU_JUST_OPENED_KEY), true);
             });
         }
 
@@ -602,7 +603,7 @@ impl eframe::App for CompanionApp {
                 render_session(ui, ctx, &session, &agent_frames, self.size, win_w, win_h);
             });
 
-        render_size_picker(ctx);
+        render_size_picker(ctx, win_w, win_h);
         ctx.request_repaint_after(Duration::from_millis(16));
     }
 }
@@ -685,7 +686,7 @@ fn render_session(
     );
 }
 
-fn render_size_picker(ctx: &egui::Context) {
+fn render_size_picker(ctx: &egui::Context, win_w: f32, win_h: f32) {
     let open: bool = ctx.data(|d| d.get_temp(egui::Id::new(MENU_OPEN_KEY)).unwrap_or(false));
     if !open {
         return;
@@ -701,80 +702,101 @@ fn render_size_picker(ctx: &egui::Context) {
             .unwrap_or([20.0, 20.0])
     });
     let size: f32 = ctx.data(|d| d.get_temp(egui::Id::new(SIZE_KEY)).unwrap_or(DEFAULT_SIZE));
+    let x = pos[0].clamp(MENU_PAD, (win_w - MENU_W - MENU_PAD).max(MENU_PAD));
+    let y = pos[1].clamp(MENU_PAD, (win_h - MENU_H - MENU_PAD).max(MENU_PAD));
+
+    let response =
+        egui::Area::new(egui::Id::new("size_picker"))
+            .fixed_pos(egui::pos2(x, y))
+            .order(egui::Order::Foreground)
+            .show(ctx, |ui| {
+                egui::Frame::none()
+                    .fill(egui::Color32::from_rgba_premultiplied(22, 22, 24, 245))
+                    .stroke(egui::Stroke::new(1.0, egui::Color32::from_gray(82)))
+                    .inner_margin(egui::Margin::symmetric(4.0, 4.0))
+                    .show(ui, |ui| {
+                        ui.set_min_width(MENU_W - MENU_PAD * 2.0);
+                        ui.spacing_mut().item_spacing = egui::vec2(1.0, 2.0);
+                        ui.label(
+                            egui::RichText::new("Size")
+                                .size(9.0)
+                                .color(egui::Color32::from_rgb(165, 165, 170)),
+                        );
+
+                        ui.horizontal(|ui| {
+                            for (label, preset) in SIZE_PRESETS {
+                                let active = (size - preset).abs() < 0.5;
+                                let fill = if active {
+                                    egui::Color32::from_rgb(78, 92, 122)
+                                } else {
+                                    egui::Color32::from_rgb(38, 38, 42)
+                                };
+                                let text = egui::RichText::new(*label).size(11.0).strong().color(
+                                    if active {
+                                        egui::Color32::WHITE
+                                    } else {
+                                        egui::Color32::from_rgb(210, 210, 214)
+                                    },
+                                );
+                                if ui
+                                    .add_sized(
+                                        [17.0, 18.0],
+                                        egui::Button::new(text)
+                                            .fill(fill)
+                                            .stroke(egui::Stroke::NONE),
+                                    )
+                                    .clicked()
+                                {
+                                    ctx.data_mut(|d| {
+                                        d.insert_temp(egui::Id::new(SIZE_KEY), *preset);
+                                        d.insert_temp(egui::Id::new(MENU_OPEN_KEY), false);
+                                    });
+                                }
+                            }
+                        });
+
+                        ui.add_space(1.0);
 
-    let response = egui::Area::new(egui::Id::new("size_picker"))
-        .fixed_pos(egui::pos2(pos[0], pos[1]))
-        .order(egui::Order::Foreground)
-        .show(ctx, |ui| {
-            egui::Frame::none()
-                .fill(egui::Color32::from_rgb(28, 28, 28))
-                .stroke(egui::Stroke::new(1.0, egui::Color32::from_gray(70)))
-                .inner_margin(egui::Margin::same(8.0))
-                .show(ui, |ui| {
-                    ui.label(
-                        egui::RichText::new("Window size")
-                            .size(11.0)
-                            .color(egui::Color32::from_rgb(160, 160, 160)),
-                    );
-                    ui.add_space(4.0);
-
-                    for (label, preset) in SIZE_PRESETS {
-                        let active = (size - preset).abs() < 0.5;
-                        let text = if active {
-                            egui::RichText::new(*label)
-                                .size(12.0)
-                                .strong()
-                                .color(egui::Color32::WHITE)
-                        } else {
-                            egui::RichText::new(*label)
-                                .size(12.0)
-                                .color(egui::Color32::from_rgb(200, 200, 200))
-                        };
                         if ui
-                            .add_sized([144.0, 20.0], egui::Button::new(text).frame(false))
+                            .add_sized(
+                                [MENU_W - MENU_PAD * 2.0, 17.0],
+                                egui::Button::new(
+                                    egui::RichText::new("Close")
+                                        .size(11.0)
+                                        .color(egui::Color32::from_rgb(235, 120, 120)),
+                                )
+                                .fill(egui::Color32::from_rgb(42, 32, 34))
+                                .stroke(egui::Stroke::NONE),
+                            )
                             .clicked()
                         {
                             ctx.data_mut(|d| {
-                                d.insert_temp(egui::Id::new(SIZE_KEY), *preset);
                                 d.insert_temp(egui::Id::new(MENU_OPEN_KEY), false);
+                                d.insert_temp(egui::Id::new("companion_quit"), true);
                             });
                         }
-                    }
+                    });
+            });
 
-                    ui.add_space(4.0);
-                    ui.separator();
-                    ui.add_space(2.0);
-
-                    if ui
-                        .add_sized(
-                            [144.0, 20.0],
-                            egui::Button::new(
-                                egui::RichText::new("Close companion")
-                                    .size(12.0)
-                                    .color(egui::Color32::from_rgb(220, 100, 100)),
-                            )
-                            .frame(false),
-                        )
-                        .clicked()
-                    {
-                        ctx.data_mut(|d| {
-                            d.insert_temp(egui::Id::new(MENU_OPEN_KEY), false);
-                            d.insert_temp(egui::Id::new("companion_quit"), true);
-                        });
-                    }
-                });
-        });
+    let just_opened = ctx.data_mut(|d| {
+        let id = egui::Id::new(MENU_JUST_OPENED_KEY);
+        let just_opened = d.get_temp::<bool>(id).unwrap_or(false);
+        d.insert_temp(id, false);
+        just_opened
+    });
+    if !just_opened && clicked_outside_menu(ctx, response.response.rect) {
+        ctx.data_mut(|d| d.insert_temp(egui::Id::new(MENU_OPEN_KEY), false));
+    }
+}
 
-    let clicked_outside = ctx.input(|i| {
+fn clicked_outside_menu(ctx: &egui::Context, menu_rect: egui::Rect) -> bool {
+    ctx.input(|i| {
         (i.pointer.primary_released() || i.pointer.secondary_released())
             && i.pointer
                 .interact_pos()
-                .map(|pos| !response.response.rect.contains(pos))
+                .map(|pos| !menu_rect.contains(pos))
                 .unwrap_or(false)
-    });
-    if clicked_outside {
-        ctx.data_mut(|d| d.insert_temp(egui::Id::new(MENU_OPEN_KEY), false));
-    }
+    })
 }
 
 fn fit_text(ctx: &egui::Context, text: &str, font_id: &egui::FontId, max_width: f32) -> String {