nandi/cosmicnimpublic Fork
⑂ 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.

Download ZIP

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