nandi/cosmicnimpublic Fork 0
2d2278d
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.

Tell the host how much space it actually has

The window came up 948x952 under niri, not the 420x640 the config asked
for — a tiling compositor decides that, not us. With keys at a fixed size
the keypad sat in the corner of a window twice its size, and asking for
Length::Fill did not help: it widens a container but not a cosmic button,
which is why the keys kept their label width while the fixed heights took.

So on_view now receives the space on offer, via iced's responsive, and the
calculator divides it into a 4x5 grid with floors. The host doing the
arithmetic is also the only version that survives a resize.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
nandithebull committed 2026-09-20T11:30:19-07:00 Browse files
2d2278d parent: 845f2a9
modified cosmic_ffi/Cargo.lock +1 -1
@@ -964,7 +964,7 @@ dependencies = [
964964
965965 [[package]]
966966 name = "cosmic_ffi"
967-version = "0.4.0"
967+version = "0.5.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.4.0"967+version = "0.5.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.4.0"
3+version = "0.5.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.4.0"3+version = "0.5.0"
4 edition = "2021"4 edition = "2021"
5 5
6 [lib]6 [lib]
modified cosmic_ffi/cosmic_ffi.h +4 -1
@@ -14,7 +14,10 @@
1414 /* Opaque. Valid only for the duration of the on_view call it arrived with. */
1515 typedef struct CosmicBuilder CosmicBuilder;
1616
17-typedef void (*cosmic_on_view)(void *ctx, CosmicBuilder *builder);
17+/* `width`/`height` are the pixels actually available, which is not the size
18+ requested in CosmicConfig when a tiling compositor has its own ideas. */
19+typedef void (*cosmic_on_view)(void *ctx, CosmicBuilder *builder, float width,
20+ float height);
1821 typedef void (*cosmic_on_press)(void *ctx, int32_t id);
1922
2023 typedef struct {
@@ -14,7 +14,10 @@
14 /* Opaque. Valid only for the duration of the on_view call it arrived with. */14 /* Opaque. Valid only for the duration of the on_view call it arrived with. */
15 typedef struct CosmicBuilder CosmicBuilder;15 typedef struct CosmicBuilder CosmicBuilder;
16 16
17-typedef void (*cosmic_on_view)(void *ctx, CosmicBuilder *builder);17+/* `width`/`height` are the pixels actually available, which is not the size
18+ requested in CosmicConfig when a tiling compositor has its own ideas. */
19+typedef void (*cosmic_on_view)(void *ctx, CosmicBuilder *builder, float width,
20+ float height);
18 typedef void (*cosmic_on_press)(void *ctx, int32_t id);21 typedef void (*cosmic_on_press)(void *ctx, int32_t id);
19 22
20 typedef struct {23 typedef struct {
modified cosmic_ffi/src/lib.rs +11 -6
@@ -15,9 +15,12 @@ use std::ffi::{c_char, c_void, CStr};
1515 use cosmic::iced::{Alignment, Length};
1616 use cosmic::widget;
1717
18-/// Describes the widget tree for one frame. Only valid for the duration of
19-/// the `on_view` call it was handed to.
20-pub type OnView = Option<unsafe extern "C" fn(ctx: *mut c_void, builder: *mut Builder)>;
18+/// Describes the widget tree for one frame. The builder is only valid for the
19+/// duration of the call; `width` and `height` are the space available in
20+/// pixels, so the host can lay out against the real window rather than the
21+/// size it asked for — a tiling compositor rarely grants that size.
22+pub type OnView =
23+ Option<unsafe extern "C" fn(ctx: *mut c_void, builder: *mut Builder, width: f32, height: f32)>;
2124
2225 /// Called when the button carrying `id` is pressed.
2326 pub type OnPress = Option<unsafe extern "C" fn(ctx: *mut c_void, id: i32)>;
@@ -383,10 +386,10 @@ struct Host {
383386 unsafe impl Send for Host {}
384387
385388 impl Host {
386- fn view(&self) -> Element {
389+ fn view(&self, width: f32, height: f32) -> Element {
387390 let mut builder = Builder::new();
388391 if let Some(cb) = self.on_view {
389- unsafe { cb(self.ctx, &mut builder) };
392+ unsafe { cb(self.ctx, &mut builder, width, height) };
390393 }
391394 builder.finish()
392395 }
@@ -439,7 +442,9 @@ impl cosmic::Application for App {
439442 }
440443
441444 fn view(&self) -> cosmic::Element<Message> {
442- self.flags.host.view()
445+ // `responsive` hands the closure the space actually on offer, which is
446+ // the only way the host can know it.
447+ widget::responsive(|size| self.flags.host.view(size.width, size.height)).into()
443448 }
444449 }
445450
@@ -15,9 +15,12 @@ use std::ffi::{c_char, c_void, CStr};
15 use cosmic::iced::{Alignment, Length};15 use cosmic::iced::{Alignment, Length};
16 use cosmic::widget;16 use cosmic::widget;
17 17
18-/// Describes the widget tree for one frame. Only valid for the duration of18+/// Describes the widget tree for one frame. The builder is only valid for the
19-/// the `on_view` call it was handed to.19+/// duration of the call; `width` and `height` are the space available in
20-pub type OnView = Option<unsafe extern "C" fn(ctx: *mut c_void, builder: *mut Builder)>;20+/// pixels, so the host can lay out against the real window rather than the
21+/// size it asked for — a tiling compositor rarely grants that size.
22+pub type OnView =
23+ Option<unsafe extern "C" fn(ctx: *mut c_void, builder: *mut Builder, width: f32, height: f32)>;
21 24
22 /// Called when the button carrying `id` is pressed.25 /// Called when the button carrying `id` is pressed.
23 pub type OnPress = Option<unsafe extern "C" fn(ctx: *mut c_void, id: i32)>;26 pub type OnPress = Option<unsafe extern "C" fn(ctx: *mut c_void, id: i32)>;
@@ -383,10 +386,10 @@ struct Host {
383 unsafe impl Send for Host {}386 unsafe impl Send for Host {}
384 387
385 impl Host {388 impl Host {
386- fn view(&self) -> Element {389+ fn view(&self, width: f32, height: f32) -> Element {
387 let mut builder = Builder::new();390 let mut builder = Builder::new();
388 if let Some(cb) = self.on_view {391 if let Some(cb) = self.on_view {
389- unsafe { cb(self.ctx, &mut builder) };392+ unsafe { cb(self.ctx, &mut builder, width, height) };
390 }393 }
391 builder.finish()394 builder.finish()
392 }395 }
@@ -439,7 +442,9 @@ impl cosmic::Application for App {
439 }442 }
440 443
441 fn view(&self) -> cosmic::Element<Message> {444 fn view(&self) -> cosmic::Element<Message> {
442- self.flags.host.view()445+ // `responsive` hands the closure the space actually on offer, which is
446+ // the only way the host can know it.
447+ widget::responsive(|size| self.flags.host.view(size.width, size.height)).into()
443 }448 }
444 }449 }
445 450
modified cosmicnim.nimble +1 -1
@@ -1,4 +1,4 @@
1-version = "0.4.0"
1+version = "0.5.0"
22 author = "nandi"
33 description = "libcosmic behind a C ABI, driven from Nim"
44 license = "MIT"
@@ -1,4 +1,4 @@
1-version = "0.4.0"1+version = "0.5.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 +20 -10
@@ -24,9 +24,10 @@ type
2424 error: bool
2525
2626 const
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
27+ Cols = 4
28+ Rows = 5
29+ MinKeyW = 56.0
30+ MinKeyH = 44.0
3031
3132 # 0..9 are the digit keys, so their id is their value.
3233 IdDot* = 10'i32
@@ -151,9 +152,20 @@ proc onPress*(ctx: pointer; id: int32) {.cdecl.} =
151152 else:
152153 discard
153154
154-proc onView(ctx: pointer; b: Builder) {.cdecl.} =
155+proc onView(ctx: pointer; b: Builder; width, height: cfloat) {.cdecl.} =
155156 let c = cast[ptr Calc](ctx)
156157
158+ # Size the keys from the space actually on offer, so the keypad fills the
159+ # window however the compositor decided to size it. Length::Fill does not
160+ # widen a cosmic button, so the host does the arithmetic instead.
161+ let
162+ gap = space(SpaceXs)
163+ pad = space(SpaceM)
164+ display = 96.0
165+ keyW = max(MinKeyW, (float(width) - 2 * pad - float(Cols - 1) * gap) / float(Cols))
166+ keyH = max(MinKeyH,
167+ (float(height) - display - 2 * pad - float(Rows - 1) * gap) / float(Rows))
168+
157169 b.container:
158170 b.fill()
159171 b.alignCenter()
@@ -166,19 +178,17 @@ proc onView(ctx: pointer; b: Builder) {.cdecl.} =
166178 b.text(c[].display, TextTitle1)
167179
168180 b.column:
169- b.fill()
170- b.spacing(space(SpaceXs))
181+ b.spacing(gap)
171182 for keyRow in Keypad:
172183 b.row:
173- b.spacing(space(SpaceXs))
184+ b.spacing(gap)
174185 b.alignCenter()
175- b.fill()
176186 for key in keyRow:
177- b.size(Fill, KeyH)
187+ b.size(keyW, keyH)
178188 if key.label.len > 0:
179189 b.button(key.label, key.id, key.style)
180190 else:
181- b.space(0, KeyH) # hold the column open
191+ b.space(keyW, keyH) # hold the column open
182192
183193 proc main() =
184194 var calc = newCalc()
@@ -24,9 +24,10 @@ type
24 error: bool24 error: bool
25 25
26 const26 const
27- # Keys fill their row, so the four columns split the width evenly and grow27+ Cols = 4
28- # with the window instead of each key hugging its own label.28+ Rows = 5
29- KeyH = 84.029+ MinKeyW = 56.0
30+ MinKeyH = 44.0
30 31
31 # 0..9 are the digit keys, so their id is their value.32 # 0..9 are the digit keys, so their id is their value.
32 IdDot* = 10'i3233 IdDot* = 10'i32
@@ -151,9 +152,20 @@ proc onPress*(ctx: pointer; id: int32) {.cdecl.} =
151 else:152 else:
152 discard153 discard
153 154
154-proc onView(ctx: pointer; b: Builder) {.cdecl.} =155+proc onView(ctx: pointer; b: Builder; width, height: cfloat) {.cdecl.} =
155 let c = cast[ptr Calc](ctx)156 let c = cast[ptr Calc](ctx)
156 157
158+ # Size the keys from the space actually on offer, so the keypad fills the
159+ # window however the compositor decided to size it. Length::Fill does not
160+ # widen a cosmic button, so the host does the arithmetic instead.
161+ let
162+ gap = space(SpaceXs)
163+ pad = space(SpaceM)
164+ display = 96.0
165+ keyW = max(MinKeyW, (float(width) - 2 * pad - float(Cols - 1) * gap) / float(Cols))
166+ keyH = max(MinKeyH,
167+ (float(height) - display - 2 * pad - float(Rows - 1) * gap) / float(Rows))
168+
157 b.container:169 b.container:
158 b.fill()170 b.fill()
159 b.alignCenter()171 b.alignCenter()
@@ -166,19 +178,17 @@ proc onView(ctx: pointer; b: Builder) {.cdecl.} =
166 b.text(c[].display, TextTitle1)178 b.text(c[].display, TextTitle1)
167 179
168 b.column:180 b.column:
169- b.fill()181+ b.spacing(gap)
170- b.spacing(space(SpaceXs))
171 for keyRow in Keypad:182 for keyRow in Keypad:
172 b.row:183 b.row:
173- b.spacing(space(SpaceXs))184+ b.spacing(gap)
174 b.alignCenter()185 b.alignCenter()
175- b.fill()
176 for key in keyRow:186 for key in keyRow:
177- b.size(Fill, KeyH)187+ b.size(keyW, keyH)
178 if key.label.len > 0:188 if key.label.len > 0:
179 b.button(key.label, key.id, key.style)189 b.button(key.label, key.id, key.style)
180 else:190 else:
181- b.space(0, KeyH) # hold the column open191+ b.space(keyW, keyH) # hold the column open
182 192
183 proc main() =193 proc main() =
184 var calc = newCalc()194 var calc = newCalc()
modified nim/cosmicnim.nim +3 -1
@@ -31,7 +31,9 @@ type
3131 Builder* = distinct pointer
3232 ## Opaque. Only valid for the duration of the `onView` call it arrived in.
3333
34- OnView* = proc (ctx: pointer; b: Builder) {.cdecl.}
34+ OnView* = proc (ctx: pointer; b: Builder; width, height: cfloat) {.cdecl.}
35+ ## `width`/`height` are the pixels actually available — not what
36+ ## CosmicConfig asked for, which a tiling compositor will override.
3537 OnPress* = proc (ctx: pointer; id: int32) {.cdecl.}
3638
3739 CosmicConfig* = object
@@ -31,7 +31,9 @@ type
31 Builder* = distinct pointer31 Builder* = distinct pointer
32 ## Opaque. Only valid for the duration of the `onView` call it arrived in.32 ## Opaque. Only valid for the duration of the `onView` call it arrived in.
33 33
34- OnView* = proc (ctx: pointer; b: Builder) {.cdecl.}34+ OnView* = proc (ctx: pointer; b: Builder; width, height: cfloat) {.cdecl.}
35+ ## `width`/`height` are the pixels actually available — not what
36+ ## CosmicConfig asked for, which a tiling compositor will override.
35 OnPress* = proc (ctx: pointer; id: int32) {.cdecl.}37 OnPress* = proc (ctx: pointer; id: int32) {.cdecl.}
36 38
37 CosmicConfig* = object39 CosmicConfig* = object