git.lucas.co / cce-browser
web browser (Servo)
git clone https://git.lucas.co/cce-browser.git

commit8ee81c5045723e2adc23153bf359794c399ffa0e
parent85f04f4cfd
authorLucas Galante <[email protected]>
date2026-08-10 23:50
Float the utility bar above the page content

The page now fills the whole window and the bar becomes a rounded
island inset 10px from the top, drawn as a blur-behind plate (negative
alpha) so it frosts the page beneath. Input hit-testing switches from
a y < CHROME_H split to the bar rect: pointer/wheel/click coordinates
forward to the webview unoffset, and events over the bar stay in the
chrome. Loading strip clips to the bar's rounded outline.

Live-verified: page scrolls beneath the bar, link hover/clicks land at
the new coordinates.

Co-Authored-By: Claude Fable 5 <[email protected]>

 src/main.rs | 90 +++++++++++++++++++++++++++++++++----------------------------
 1 file changed, 49 insertions(+), 41 deletions(-)

diff --git a/src/main.rs b/src/main.rs
index c0a97ce..c50d18b 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -19,11 +19,16 @@ use cce_ui::widget::{ElementState, Key, KeyEvent, MouseButton, MouseScrollDelta,
 
 use webview::ServoHost;
 
-const CHROME_H: f32 = 42.0;
+const BAR_MARGIN: f32 = 10.0;
+const BAR_H: f32 = 38.0;
+const BAR_RADIUS: f32 = 10.0;
+const BAR_PAD: f32 = 7.0;
+/// Utility-bar fill; the negative alpha marks the plate as blur-behind, so
+/// it frosts the page content drawn beneath it (|alpha| = blur strength).
+const BAR_FILL: [f32; 4] = [0.11, 0.12, 0.13, -0.58];
 const BTN_W: f32 = 30.0;
 const BTN_H: f32 = 26.0;
 const BTN_GAP: f32 = 6.0;
-const PAD: f32 = 8.0;
 const URL_FONT: f32 = 14.0;
 const URL_PAD_X: f32 = 9.0;
 /// Pixels per wheel notch when the DE reports discrete line deltas.
@@ -31,10 +36,9 @@ const LINE_PX: f64 = 76.0;
 
 const HOME_URL: &str = "https://servo.org";
 
-const CHROME_BG: [f32; 4] = [0.13, 0.14, 0.15, 1.0];
 const PAGE_BG: [f32; 4] = [0.10, 0.10, 0.11, 1.0];
-const FIELD_BG: [f32; 4] = [0.09, 0.09, 0.10, 1.0];
-const BTN_BG: [f32; 4] = [0.18, 0.19, 0.21, 1.0];
+const FIELD_BG: [f32; 4] = [0.09, 0.09, 0.10, 0.85];
+const BTN_BG: [f32; 4] = [0.20, 0.21, 0.23, 0.85];
 const RIM: [f32; 4] = [0.22, 0.23, 0.25, 1.0];
 const RIM_FOCUS: [f32; 4] = [0.33, 0.48, 0.72, 1.0];
 const ACCENT: [f32; 4] = [0.35, 0.55, 0.85, 1.0];
@@ -67,21 +71,32 @@ fn hit(r: &Rect, x: f32, y: f32) -> bool {
     x >= r.x && x < r.x + r.width && y >= r.y && y < r.y + r.height
 }
 
+/// The floating utility bar, overlaid on the page content.
+fn bar_rect(win_w: f32) -> Rect {
+    Rect {
+        x: BAR_MARGIN,
+        y: BAR_MARGIN,
+        width: (win_w - 2.0 * BAR_MARGIN).max(120.0),
+        height: BAR_H,
+    }
+}
+
 fn btn_rect(i: usize) -> Rect {
     Rect {
-        x: PAD + i as f32 * (BTN_W + BTN_GAP),
-        y: (CHROME_H - BTN_H) / 2.0,
+        x: BAR_MARGIN + BAR_PAD + i as f32 * (BTN_W + BTN_GAP),
+        y: BAR_MARGIN + (BAR_H - BTN_H) / 2.0,
         width: BTN_W,
         height: BTN_H,
     }
 }
 
 fn url_rect(win_w: f32) -> Rect {
-    let x = PAD + 3.0 * (BTN_W + BTN_GAP) + 4.0;
+    let bar = bar_rect(win_w);
+    let x = BAR_MARGIN + BAR_PAD + 3.0 * (BTN_W + BTN_GAP) + 4.0;
     Rect {
         x,
-        y: (CHROME_H - BTN_H) / 2.0,
-        width: (win_w - x - PAD).max(60.0),
+        y: BAR_MARGIN + (BAR_H - BTN_H) / 2.0,
+        width: (bar.x + bar.width - BAR_PAD - x).max(60.0),
         height: BTN_H,
     }
 }
@@ -167,20 +182,11 @@ fn next_boundary(s: &str, i: usize) -> usize {
 }
 
 impl BrowserApp {
-    fn content_rect(&self) -> Rect {
-        Rect {
-            x: 0.0,
-            y: CHROME_H,
-            width: self.win.0,
-            height: (self.win.1 - CHROME_H).max(1.0),
-        }
-    }
-
+    /// The page fills the whole window; the utility bar floats above it.
     fn content_px(&self) -> (u32, u32) {
-        let r = self.content_rect();
         (
-            (r.width as f64 * self.scale) as u32,
-            (r.height as f64 * self.scale) as u32,
+            (self.win.0 as f64 * self.scale) as u32,
+            (self.win.1 as f64 * self.scale) as u32,
         )
     }
 
@@ -269,7 +275,7 @@ impl Application for BrowserApp {
 
     fn new(_qh: &QueueHandle<EngineState<Self>>, sender: calloop::channel::Sender<Self::Message>) -> Self {
         let url = Url::parse(HOME_URL).expect("home url");
-        let host = ServoHost::new(sender, url, (1200, (800.0 - CHROME_H) as u32));
+        let host = ServoHost::new(sender, url, (1200, 800));
         Self {
             host,
             win: (1200.0, 800.0),
@@ -319,9 +325,9 @@ impl Application for BrowserApp {
 
     fn handle_pointer_move(&mut self, pos: LogicalPosition, _needs_rebuild: &mut bool) {
         self.pointer = (pos.x, pos.y);
-        if pos.y >= CHROME_H {
+        if !hit(&bar_rect(self.win.0), pos.x, pos.y) {
             let s = self.scale as f32;
-            self.host.mouse_move(pos.x * s, (pos.y - CHROME_H) * s);
+            self.host.mouse_move(pos.x * s, pos.y * s);
         }
     }
 
@@ -334,7 +340,7 @@ impl Application for BrowserApp {
     ) -> Option<Self::Message> {
         let pressed = state == ElementState::Pressed;
 
-        if pos.y < CHROME_H {
+        if hit(&bar_rect(self.win.0), pos.x, pos.y) {
             if !pressed || button != MouseButton::Left {
                 return None;
             }
@@ -369,7 +375,7 @@ impl Application for BrowserApp {
             _ => {
                 if let Some(b) = dom_button(button) {
                     let s = self.scale as f32;
-                    self.host.mouse_button(b, pressed, pos.x * s, (pos.y - CHROME_H) * s);
+                    self.host.mouse_button(b, pressed, pos.x * s, pos.y * s);
                 }
             }
         }
@@ -377,7 +383,7 @@ impl Application for BrowserApp {
     }
 
     fn handle_mouse_wheel(&mut self, delta: &MouseScrollDelta, pos: LogicalPosition, _needs_rebuild: &mut bool) {
-        if pos.y < CHROME_H {
+        if hit(&bar_rect(self.win.0), pos.x, pos.y) {
             return;
         }
         // WheelDelta keeps cce-ui's winit sign convention (positive = scroll
@@ -388,12 +394,7 @@ impl Application for BrowserApp {
             MouseScrollDelta::PixelDelta(p) => (p.x, p.y),
         };
         let s = self.scale;
-        self.host.wheel(
-            dx * s,
-            dy * s,
-            pos.x * s as f32,
-            (pos.y - CHROME_H) * s as f32,
-        );
+        self.host.wheel(dx * s, dy * s, pos.x * s as f32, pos.y * s as f32);
     }
 
     fn handle_key_input(&mut self, event: &KeyEvent, needs_rebuild: &mut bool) -> Option<Self::Message> {
@@ -440,19 +441,26 @@ impl Application for BrowserApp {
         let mut pc = PaintCtx::new();
         let w = size.width;
 
-        // Page.
-        let content = self.content_rect();
+        // Page: full-bleed under the floating bar.
+        let content = Rect { x: 0.0, y: 0.0, width: w, height: size.height };
         pc.quad(content, PAGE_BG);
         if let Some((id, ..)) = self.host.image() {
             pc.image(id, content, 1.0);
         } else {
-            pc.text("Loading...", content.x + 16.0, content.y + 18.0, 13.0, TEXT_DIM);
+            pc.text("Loading...", BAR_MARGIN + 6.0, BAR_MARGIN + BAR_H + 22.0, 13.0, TEXT_DIM);
         }
 
-        // Chrome bar.
-        pc.quad(Rect { x: 0.0, y: 0.0, width: w, height: CHROME_H }, CHROME_BG);
+        // Floating utility bar: a blur-behind plate frosting the page under it.
+        let bar = bar_rect(w);
+        let radii = (BAR_RADIUS, BAR_RADIUS, BAR_RADIUS, BAR_RADIUS);
+        pc.plate(bar, radii, BAR_FILL, cce_ui::layout::bevel_width().min(4.0));
         if self.loading {
-            pc.quad(Rect { x: 0.0, y: CHROME_H - 2.0, width: w, height: 2.0 }, ACCENT);
+            pc.clip_rounded(bar, BAR_RADIUS, |pc| {
+                pc.quad(
+                    Rect { x: bar.x, y: bar.y + bar.height - 2.0, width: bar.width, height: 2.0 },
+                    ACCENT,
+                );
+            });
         }
 
         let labels = ["<", ">", "R"];
@@ -503,7 +511,7 @@ impl Application for BrowserApp {
     }
 
     fn clear_color(&self) -> [f32; 4] {
-        CHROME_BG
+        PAGE_BG
     }
 }