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>
845f2a9 parent: dee3349 modified
cosmic_ffi/Cargo.lock +1 -1 | @@ -964,7 +964,7 @@ dependencies = [ | ||
| 964 | 964 | |
| 965 | 965 | [[package]] |
| 966 | 966 | name = "cosmic_ffi" |
| 967 | -version = "0.3.0" | |
| 967 | +version = "0.4.0" | |
| 968 | 968 | dependencies = [ |
| 969 | 969 | "libcosmic", |
| 970 | 970 | ] |
| @@ -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 @@ | ||
| 1 | 1 | [package] |
| 2 | 2 | name = "cosmic_ffi" |
| 3 | -version = "0.3.0" | |
| 3 | +version = "0.4.0" | |
| 4 | 4 | edition = "2021" |
| 5 | 5 | |
| 6 | 6 | [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 @@ | ||
| 7 | 7 | #ifndef COSMIC_FFI_H |
| 8 | 8 | #define COSMIC_FFI_H |
| 9 | 9 | |
| 10 | +#include <math.h> | |
| 10 | 11 | #include <stddef.h> |
| 11 | 12 | #include <stdint.h> |
| 12 | 13 | |
| @@ -63,8 +64,10 @@ void cosmic_button(CosmicBuilder *b, int32_t style, const char *label, | ||
| 63 | 64 | |
| 64 | 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 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) | |
| 68 | 71 | void cosmic_size(CosmicBuilder *b, float w, float h); |
| 69 | 72 | |
| 70 | 73 | /* --- the active theme's spacing scale, for laying out in COSMIC's rhythm --- */ |
| @@ -7,6 +7,7 @@ | |||
| 7 | #ifndef COSMIC_FFI_H | 7 | #ifndef COSMIC_FFI_H |
| 8 | #define COSMIC_FFI_H | 8 | #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 its | 67 | +/* 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 { | ||
| 155 | 155 | /// Take the pending size, resetting it so it applies to one leaf only. |
| 156 | 156 | fn take_size(&mut self) -> (Option<Length>, Option<Length>) { |
| 157 | 157 | 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)) | |
| 162 | 159 | } |
| 163 | 160 | |
| 164 | 161 | fn leaf(&mut self, element: Element) { |
| @@ -233,20 +230,37 @@ pub unsafe extern "C" fn cosmic_fill(b: *mut Builder) { | ||
| 233 | 230 | builder!(b).top().fill = true; |
| 234 | 231 | } |
| 235 | 232 | |
| 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. | |
| 238 | 248 | #[no_mangle] |
| 239 | 249 | pub unsafe extern "C" fn cosmic_size(b: *mut Builder, w: f32, h: f32) { |
| 240 | 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 | 256 | #[no_mangle] |
| 245 | 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 | 261 | 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))) | |
| 250 | 264 | .into(), |
| 251 | 265 | ) |
| 252 | 266 | } |
| @@ -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 at | 233 | +/// 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" | |
| 2 | 2 | author = "nandi" |
| 3 | 3 | description = "libcosmic behind a C ABI, driven from Nim" |
| 4 | 4 | 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 | ||
| 24 | 24 | error: bool |
| 25 | 25 | |
| 26 | 26 | 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 | |
| 31 | 30 | |
| 32 | 31 | # 0..9 are the digit keys, so their id is their value. |
| 33 | 32 | IdDot* = 10'i32 |
| @@ -167,17 +166,19 @@ proc onView(ctx: pointer; b: Builder) {.cdecl.} = | ||
| 167 | 166 | b.text(c[].display, TextTitle1) |
| 168 | 167 | |
| 169 | 168 | b.column: |
| 169 | + b.fill() | |
| 170 | 170 | b.spacing(space(SpaceXs)) |
| 171 | 171 | for keyRow in Keypad: |
| 172 | 172 | b.row: |
| 173 | 173 | b.spacing(space(SpaceXs)) |
| 174 | 174 | b.alignCenter() |
| 175 | + b.fill() | |
| 175 | 176 | for key in keyRow: |
| 176 | - b.size(KeyW, KeyH) | |
| 177 | + b.size(Fill, KeyH) | |
| 177 | 178 | if key.label.len > 0: |
| 178 | 179 | b.button(key.label, key.id, key.style) |
| 179 | 180 | else: |
| 180 | - b.space(KeyW, KeyH) # hold the column open | |
| 181 | + b.space(0, KeyH) # hold the column open | |
| 181 | 182 | |
| 182 | 183 | proc main() = |
| 183 | 184 | var calc = newCalc() |
| @@ -186,8 +187,8 @@ proc main() = | ||
| 186 | 187 | onView: onView, |
| 187 | 188 | onPress: onPress, |
| 188 | 189 | ctx: addr calc, |
| 189 | - width: 380, | |
| 190 | - height: 560, | |
| 190 | + width: 420, | |
| 191 | + height: 640, | |
| 191 | 192 | ) |
| 192 | 193 | let rc = cosmicRun(addr config) |
| 193 | 194 | if rc != 0: |
| @@ -24,10 +24,9 @@ type | |||
| 24 | error: bool | 24 | error: bool |
| 25 | 25 | ||
| 26 | const | 26 | const |
| 27 | - # A keypad wants a grid, and the ABI sizes leaves in pixels, so the keys | 27 | + # 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.0 | 29 | + 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'i32 | 32 | 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 open | 181 | + 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; | ||
| 118 | 118 | |
| 119 | 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 | 125 | 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. | |
| 124 | 129 | rawSize(b, cfloat(w), cfloat(h)) |
| 125 | 130 | |
| 126 | 131 | 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 = |