nandi/cosmicnimpublic Fork 0
845f2a9
Commits
Clone
git clone https://git.rickub.com/nandi/cosmicnim.git
git clone ssh://git@rickub.com/nandi/cosmicnim.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

Let leaves fill, so the keypad is a grid rather than a row of labels

Fixed pixel keys do not adapt to the window, and picking a bigger number
only moves the problem. INFINITY as a dimension now means Length::Fill, so
the four keys in a row split the width evenly and grow with the window.

cosmic_space now consumes the pending size like every other leaf. It did
not before, so a size set for a spacer leaked onto the next row's first
key — which is exactly what the keypad's one empty cell would have done.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandithebull committed 2026-09-20T11:22:26-07:00 Browse files
845f2a9 parent: dee3349
modified cosmic_ffi/Cargo.lock +1 -1
@@ -964,7 +964,7 @@ dependencies = [
964964
965965 [[package]]
966966 name = "cosmic_ffi"
967-version = "0.3.0"
967+version = "0.4.0"
968968 dependencies = [
969969 "libcosmic",
970970 ]
@@ -964,7 +964,7 @@ dependencies = [
964 964
965 [[package]]965 [[package]]
966 name = "cosmic_ffi"966 name = "cosmic_ffi"
967-version = "0.3.0"967+version = "0.4.0"
968 dependencies = [968 dependencies = [
969 "libcosmic",969 "libcosmic",
970 ]970 ]
modified cosmic_ffi/Cargo.toml +1 -1
@@ -1,6 +1,6 @@
11 [package]
22 name = "cosmic_ffi"
3-version = "0.3.0"
3+version = "0.4.0"
44 edition = "2021"
55
66 [lib]
@@ -1,6 +1,6 @@
1 [package]1 [package]
2 name = "cosmic_ffi"2 name = "cosmic_ffi"
3-version = "0.3.0"3+version = "0.4.0"
4 edition = "2021"4 edition = "2021"
5 5
6 [lib]6 [lib]
modified cosmic_ffi/cosmic_ffi.h +5 -2
@@ -7,6 +7,7 @@
77 #ifndef COSMIC_FFI_H
88 #define COSMIC_FFI_H
99
10+#include <math.h>
1011 #include <stddef.h>
1112 #include <stdint.h>
1213
@@ -63,8 +64,10 @@ void cosmic_button(CosmicBuilder *b, int32_t style, const char *label,
6364
6465 void cosmic_space(CosmicBuilder *b, float w, float h);
6566
66-/* Size the NEXT leaf, in pixels; a negative dimension leaves that axis at its
67- natural size. Applies to one leaf only, then resets. */
67+/* Size the NEXT leaf, in pixels. COSMIC_FILL takes the space on offer, and
68+ siblings that all ask for it share the axis equally; a negative dimension
69+ leaves that axis natural. Applies to one leaf only, then resets. */
70+#define COSMIC_FILL ((float)INFINITY)
6871 void cosmic_size(CosmicBuilder *b, float w, float h);
6972
7073 /* --- the active theme's spacing scale, for laying out in COSMIC's rhythm --- */
@@ -7,6 +7,7 @@
7 #ifndef COSMIC_FFI_H7 #ifndef COSMIC_FFI_H
8 #define COSMIC_FFI_H8 #define COSMIC_FFI_H
9 9
10+#include <math.h>
10 #include <stddef.h>11 #include <stddef.h>
11 #include <stdint.h>12 #include <stdint.h>
12 13
@@ -63,8 +64,10 @@ void cosmic_button(CosmicBuilder *b, int32_t style, const char *label,
63 64
64 void cosmic_space(CosmicBuilder *b, float w, float h);65 void cosmic_space(CosmicBuilder *b, float w, float h);
65 66
66-/* Size the NEXT leaf, in pixels; a negative dimension leaves that axis at its67+/* Size the NEXT leaf, in pixels. COSMIC_FILL takes the space on offer, and
67- natural size. Applies to one leaf only, then resets. */68+ siblings that all ask for it share the axis equally; a negative dimension
69+ leaves that axis natural. Applies to one leaf only, then resets. */
70+#define COSMIC_FILL ((float)INFINITY)
68 void cosmic_size(CosmicBuilder *b, float w, float h);71 void cosmic_size(CosmicBuilder *b, float w, float h);
69 72
70 /* --- the active theme's spacing scale, for laying out in COSMIC's rhythm --- */73 /* --- the active theme's spacing scale, for laying out in COSMIC's rhythm --- */
modified cosmic_ffi/src/lib.rs +24 -10
@@ -155,10 +155,7 @@ impl Builder {
155155 /// Take the pending size, resetting it so it applies to one leaf only.
156156 fn take_size(&mut self) -> (Option<Length>, Option<Length>) {
157157 let (w, h) = std::mem::replace(&mut self.next_size, (-1.0, -1.0));
158- (
159- (w >= 0.0).then_some(Length::Fixed(w)),
160- (h >= 0.0).then_some(Length::Fixed(h)),
161- )
158+ (to_length(w), to_length(h))
162159 }
163160
164161 fn leaf(&mut self, element: Element) {
@@ -233,20 +230,37 @@ pub unsafe extern "C" fn cosmic_fill(b: *mut Builder) {
233230 builder!(b).top().fill = true;
234231 }
235232
236-/// Size the next leaf, in pixels. A negative dimension leaves that axis at
237-/// its natural size. Applies to one leaf only, then resets.
233+/// Infinity as a dimension means "take the space you are offered". Siblings
234+/// that all ask for it share the axis equally, which is what makes a grid.
235+fn to_length(v: f32) -> Option<Length> {
236+ if v.is_infinite() && v.is_sign_positive() {
237+ Some(Length::Fill)
238+ } else if v >= 0.0 {
239+ Some(Length::Fixed(v))
240+ } else {
241+ None
242+ }
243+}
244+
245+/// Size the next leaf, in pixels; `INFINITY` fills the available space and a
246+/// negative dimension leaves that axis natural. Applies to one leaf only,
247+/// then resets.
238248 #[no_mangle]
239249 pub unsafe extern "C" fn cosmic_size(b: *mut Builder, w: f32, h: f32) {
240250 builder!(b).next_size = (w, h);
241251 }
242252
243-/// A blank gap of `w` by `h` pixels.
253+/// A blank gap of `w` by `h` pixels. Like every leaf it consumes a pending
254+/// `cosmic_size`, which overrides these arguments — otherwise the size would
255+/// silently leak onto whatever came next.
244256 #[no_mangle]
245257 pub unsafe extern "C" fn cosmic_space(b: *mut Builder, w: f32, h: f32) {
246- builder!(b).leaf(
258+ let b = builder!(b);
259+ let (pw, ph) = b.take_size();
260+ b.leaf(
247261 widget::Space::new()
248- .width(Length::Fixed(w))
249- .height(Length::Fixed(h))
262+ .width(pw.unwrap_or(Length::Fixed(w)))
263+ .height(ph.unwrap_or(Length::Fixed(h)))
250264 .into(),
251265 )
252266 }
@@ -155,10 +155,7 @@ impl Builder {
155 /// Take the pending size, resetting it so it applies to one leaf only.155 /// Take the pending size, resetting it so it applies to one leaf only.
156 fn take_size(&mut self) -> (Option<Length>, Option<Length>) {156 fn take_size(&mut self) -> (Option<Length>, Option<Length>) {
157 let (w, h) = std::mem::replace(&mut self.next_size, (-1.0, -1.0));157 let (w, h) = std::mem::replace(&mut self.next_size, (-1.0, -1.0));
158- (158+ (to_length(w), to_length(h))
159- (w >= 0.0).then_some(Length::Fixed(w)),
160- (h >= 0.0).then_some(Length::Fixed(h)),
161- )
162 }159 }
163 160
164 fn leaf(&mut self, element: Element) {161 fn leaf(&mut self, element: Element) {
@@ -233,20 +230,37 @@ pub unsafe extern "C" fn cosmic_fill(b: *mut Builder) {
233 builder!(b).top().fill = true;230 builder!(b).top().fill = true;
234 }231 }
235 232
236-/// Size the next leaf, in pixels. A negative dimension leaves that axis at233+/// Infinity as a dimension means "take the space you are offered". Siblings
237-/// its natural size. Applies to one leaf only, then resets.234+/// that all ask for it share the axis equally, which is what makes a grid.
235+fn to_length(v: f32) -> Option<Length> {
236+ if v.is_infinite() && v.is_sign_positive() {
237+ Some(Length::Fill)
238+ } else if v >= 0.0 {
239+ Some(Length::Fixed(v))
240+ } else {
241+ None
242+ }
243+}
244+
245+/// Size the next leaf, in pixels; `INFINITY` fills the available space and a
246+/// negative dimension leaves that axis natural. Applies to one leaf only,
247+/// then resets.
238 #[no_mangle]248 #[no_mangle]
239 pub unsafe extern "C" fn cosmic_size(b: *mut Builder, w: f32, h: f32) {249 pub unsafe extern "C" fn cosmic_size(b: *mut Builder, w: f32, h: f32) {
240 builder!(b).next_size = (w, h);250 builder!(b).next_size = (w, h);
241 }251 }
242 252
243-/// A blank gap of `w` by `h` pixels.253+/// A blank gap of `w` by `h` pixels. Like every leaf it consumes a pending
254+/// `cosmic_size`, which overrides these arguments — otherwise the size would
255+/// silently leak onto whatever came next.
244 #[no_mangle]256 #[no_mangle]
245 pub unsafe extern "C" fn cosmic_space(b: *mut Builder, w: f32, h: f32) {257 pub unsafe extern "C" fn cosmic_space(b: *mut Builder, w: f32, h: f32) {
246- builder!(b).leaf(258+ let b = builder!(b);
259+ let (pw, ph) = b.take_size();
260+ b.leaf(
247 widget::Space::new()261 widget::Space::new()
248- .width(Length::Fixed(w))262+ .width(pw.unwrap_or(Length::Fixed(w)))
249- .height(Length::Fixed(h))263+ .height(ph.unwrap_or(Length::Fixed(h)))
250 .into(),264 .into(),
251 )265 )
252 }266 }
modified cosmicnim.nimble +1 -1
@@ -1,4 +1,4 @@
1-version = "0.3.0"
1+version = "0.4.0"
22 author = "nandi"
33 description = "libcosmic behind a C ABI, driven from Nim"
44 license = "MIT"
@@ -1,4 +1,4 @@
1-version = "0.3.0"1+version = "0.4.0"
2 author = "nandi"2 author = "nandi"
3 description = "libcosmic behind a C ABI, driven from Nim"3 description = "libcosmic behind a C ABI, driven from Nim"
4 license = "MIT"4 license = "MIT"
modified examples/calculator.nim +9 -8
@@ -24,10 +24,9 @@ type
2424 error: bool
2525
2626 const
27- # A keypad wants a grid, and the ABI sizes leaves in pixels, so the keys
28- # are given one size rather than each taking the width of its own label.
29- KeyW = 68.0
30- KeyH = 56.0
27+ # Keys fill their row, so the four columns split the width evenly and grow
28+ # with the window instead of each key hugging its own label.
29+ KeyH = 84.0
3130
3231 # 0..9 are the digit keys, so their id is their value.
3332 IdDot* = 10'i32
@@ -167,17 +166,19 @@ proc onView(ctx: pointer; b: Builder) {.cdecl.} =
167166 b.text(c[].display, TextTitle1)
168167
169168 b.column:
169+ b.fill()
170170 b.spacing(space(SpaceXs))
171171 for keyRow in Keypad:
172172 b.row:
173173 b.spacing(space(SpaceXs))
174174 b.alignCenter()
175+ b.fill()
175176 for key in keyRow:
176- b.size(KeyW, KeyH)
177+ b.size(Fill, KeyH)
177178 if key.label.len > 0:
178179 b.button(key.label, key.id, key.style)
179180 else:
180- b.space(KeyW, KeyH) # hold the column open
181+ b.space(0, KeyH) # hold the column open
181182
182183 proc main() =
183184 var calc = newCalc()
@@ -186,8 +187,8 @@ proc main() =
186187 onView: onView,
187188 onPress: onPress,
188189 ctx: addr calc,
189- width: 380,
190- height: 560,
190+ width: 420,
191+ height: 640,
191192 )
192193 let rc = cosmicRun(addr config)
193194 if rc != 0:
@@ -24,10 +24,9 @@ type
24 error: bool24 error: bool
25 25
26 const26 const
27- # A keypad wants a grid, and the ABI sizes leaves in pixels, so the keys27+ # Keys fill their row, so the four columns split the width evenly and grow
28- # are given one size rather than each taking the width of its own label.28+ # with the window instead of each key hugging its own label.
29- KeyW = 68.029+ KeyH = 84.0
30- KeyH = 56.0
31 30
32 # 0..9 are the digit keys, so their id is their value.31 # 0..9 are the digit keys, so their id is their value.
33 IdDot* = 10'i3232 IdDot* = 10'i32
@@ -167,17 +166,19 @@ proc onView(ctx: pointer; b: Builder) {.cdecl.} =
167 b.text(c[].display, TextTitle1)166 b.text(c[].display, TextTitle1)
168 167
169 b.column:168 b.column:
169+ b.fill()
170 b.spacing(space(SpaceXs))170 b.spacing(space(SpaceXs))
171 for keyRow in Keypad:171 for keyRow in Keypad:
172 b.row:172 b.row:
173 b.spacing(space(SpaceXs))173 b.spacing(space(SpaceXs))
174 b.alignCenter()174 b.alignCenter()
175+ b.fill()
175 for key in keyRow:176 for key in keyRow:
176- b.size(KeyW, KeyH)177+ b.size(Fill, KeyH)
177 if key.label.len > 0:178 if key.label.len > 0:
178 b.button(key.label, key.id, key.style)179 b.button(key.label, key.id, key.style)
179 else:180 else:
180- b.space(KeyW, KeyH) # hold the column open181+ b.space(0, KeyH) # hold the column open
181 182
182 proc main() =183 proc main() =
183 var calc = newCalc()184 var calc = newCalc()
@@ -186,8 +187,8 @@ proc main() =
186 onView: onView,187 onView: onView,
187 onPress: onPress,188 onPress: onPress,
188 ctx: addr calc,189 ctx: addr calc,
189- width: 380,190+ width: 420,
190- height: 560,191+ height: 640,
191 )192 )
192 let rc = cosmicRun(addr config)193 let rc = cosmicRun(addr config)
193 if rc != 0:194 if rc != 0:
modified nim/cosmicnim.nim +7 -2
@@ -118,9 +118,14 @@ proc button*(b: Builder; label: string; id: int32; style = ButtonStandard;
118118
119119 proc space*(b: Builder; w, h: float) = rawSpace(b, cfloat(w), cfloat(h))
120120
121+const Fill* = Inf
122+ ## As a dimension, take the space on offer. Siblings that all ask for it
123+ ## share the axis equally, which is how you get an even grid.
124+
121125 proc size*(b: Builder; w = -1.0; h = -1.0) =
122- ## Size the next leaf, in pixels. A negative dimension stays natural.
123- ## Applies to one leaf only, so set it before each widget you want sized.
126+ ## Size the next leaf, in pixels; `Fill` expands, a negative dimension
127+ ## stays natural. Applies to one leaf only, so set it before each widget
128+ ## you want sized.
124129 rawSize(b, cfloat(w), cfloat(h))
125130
126131 proc space*(step: SpaceStep): float =
@@ -118,9 +118,14 @@ proc button*(b: Builder; label: string; id: int32; style = ButtonStandard;
118 118
119 proc space*(b: Builder; w, h: float) = rawSpace(b, cfloat(w), cfloat(h))119 proc space*(b: Builder; w, h: float) = rawSpace(b, cfloat(w), cfloat(h))
120 120
121+const Fill* = Inf
122+ ## As a dimension, take the space on offer. Siblings that all ask for it
123+ ## share the axis equally, which is how you get an even grid.
124+
121 proc size*(b: Builder; w = -1.0; h = -1.0) =125 proc size*(b: Builder; w = -1.0; h = -1.0) =
122- ## Size the next leaf, in pixels. A negative dimension stays natural.126+ ## Size the next leaf, in pixels; `Fill` expands, a negative dimension
123- ## Applies to one leaf only, so set it before each widget you want sized.127+ ## stays natural. Applies to one leaf only, so set it before each widget
128+ ## you want sized.
124 rawSize(b, cfloat(w), cfloat(h))129 rawSize(b, cfloat(w), cfloat(h))
125 130
126 proc space*(step: SpaceStep): float =131 proc space*(step: SpaceStep): float =