desktop grid client
git clone https://git.lucas.co/cce-grid.git
lines: raised-grout relief on the rails; cells stay flat
Per-cell Recess rings expanded by the half-gap: the walls occupy exactly
the half-rail around each cell, descending from a crest at the rail
centerline to the cell edge, with the outer radius offset so the wall
stays concentric with the cell arc. Neighboring rings abut at the
centerlines — no overlap, no double-shading — so the rails read as
continuous raised grout while every cell floor stays flat. The cell loop
over-scans by one ring so border cells outside the patch still shade
their half of the boundary rails.
Co-Authored-By: Claude Fable 5 <[email protected]>
CLAUDE.md | 18 +++++++++++-------
src/main.rs | 42 ++++++++++++++++++++++++++++++++----------
2 files changed, 43 insertions(+), 17 deletions(-)
diff --git a/CLAUDE.md b/CLAUDE.md
index 2f2da94..2ddcdda 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -14,13 +14,17 @@ its own rect grid as the fallback whenever this client is absent or has not
latched a patch yet, and its gap-colored backdrop always draws beneath as
the safety net beyond patch edges.
-Rendering is a pure function of (patch, style config): flat rounded cells
-on the gap-colored rail surface — deliberately NO relief, so the grid reads
-as ground under the windows' own bevels (the compositor fallback is flat
-for the same reason: the client latch must not swap the grid's material).
-It reads the same `style.surface.desktop.*` / backplate-radius keys as the
-fallback. Keep it that way — no camera state, no timers, no input (the
-surface is input-transparent compositor-side).
+Rendering is a pure function of (patch, style config): flat rounded cells,
+with the relief on the LINES — per-cell `Recess` rings expanded by the
+half-gap, so the walls occupy exactly the half-rail around each cell
+(crest at the rail centerline, inner edge concentric with the cell arc)
+and neighboring rings abut without double-shading. Cell floors carry NO
+relief (user decision); the rails read as raised grout. The compositor
+fallback draws the same expanded-ring chamfer so the client latch never
+swaps the grid's material. It reads the same `style.surface.desktop.*` /
+backplate-radius keys as the fallback. Keep it that way — no camera
+state, no timers, no input (the surface is input-transparent
+compositor-side).
This directory is its own git repository (gitsite-published, fetch-only
origin; committing locally is publishing). `cce-grid.service` autostarts it
diff --git a/src/main.rs b/src/main.rs
index 8ca9281..c4b4aa5 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -9,8 +9,9 @@
//!
//! The look comes from the same config keys the compositor's fallback grid
//! reads (`style.surface.desktop.*`, backplate corner radius): flat
-//! rounded cells on the gap-colored rail surface — deliberately unlit, so
-//! the grid reads as ground under the windows' own relief.
+//! rounded cells, with the relief on the LINES — the gap rails read as
+//! raised grout (per-cell half-gap-expanded `Recess` rings that abut at
+//! the rail centerlines), while every cell floor stays flat.
use wayland_client::QueueHandle;
@@ -108,11 +109,24 @@ impl GridApp {
let radius = ((st.corner_radius * s)
* cce_ui::layout::corner_span_factor() as f64)
.min(cell_px / 2.0) as f32;
-
- let col0 = (p.x / period).floor() as i64;
- let col1 = ((p.x + p.w) / period).ceil() as i64;
- let row0 = (p.y / period).floor() as i64;
- let row1 = ((p.y + p.h) / period).ceil() as i64;
+ // The relief lives on the LINES, never the cells: each cell's recess
+ // rect is expanded by the half-gap, so the walls occupy exactly the
+ // half-rail around the cell — descending from a crest at the rail
+ // centerline down to the cell edge. Neighboring recesses abut at the
+ // centerlines (no overlap, no double-shading), so together the rails
+ // read as continuous raised grout while every cell floor stays flat.
+ // The outer radius is the cell arc offset outward by the same
+ // half-gap, so the wall stays concentric with the cell corner.
+ let half_gap = (st.gap_width / 2.0) * s;
+ let ring_radius = radius + half_gap as f32;
+ let ring_depth = (half_gap as f32).max(1.0);
+
+ // One extra ring of cells beyond the patch: a border cell outside the
+ // patch still owns the inner half of the boundary rail's shading.
+ let col0 = (p.x / period).floor() as i64 - 1;
+ let col1 = ((p.x + p.w) / period).ceil() as i64 + 1;
+ let row0 = (p.y / period).floor() as i64 - 1;
+ let row1 = ((p.y + p.h) / period).ceil() as i64 + 1;
let mut cells = 0usize;
for col in col0..col1 {
for row in row0..row1 {
@@ -128,10 +142,18 @@ impl GridApp {
width: (len * s) as f32,
height: (len * s) as f32,
};
- // A flat cell — no relief: the grid is ground, not furniture,
- // and a lit well under every window fought the windows' own
- // bevels.
pc.rounded_rect(rect, radius, (true, true, true, true), st.cell_color);
+ let ring = Rect {
+ x: rect.x - half_gap as f32,
+ y: rect.y - half_gap as f32,
+ width: rect.width + 2.0 * half_gap as f32,
+ height: rect.height + 2.0 * half_gap as f32,
+ };
+ pc.recess(
+ ring,
+ (ring_radius, ring_radius, ring_radius, ring_radius),
+ ring_depth,
+ );
}
}
}