Put a row's `:align :end` where the row ends
`align` on a row was read as a cross-axis gravity and handed to `align_y`, which is what a column's `align` means and not what a row's does. glimmer's meaning — the one the shared screens and frq.hiccup are written against — is the main axis: `:end` lays the children out *from* the right, so the first child in the source is the rightmost on screen. Taken as a gravity it did nothing you could see except sit the children low. frq's message heading is where that showed: its two chips came out hard against the clock, in the order ✏️ ↩️ 🙂 rather than the other way round against the right edge, and the words below lost the width the chips took. The chats screen's join and search rows had the same silent no-op. iced has no main-axis alignment on a Row, so the edge is a container's doing: the children reversed, and the row put against the right of a container that takes the width. Not a leading Fill space, which would have halved the slack with a row that already has something filling in it — the join box beside its button is exactly that row, and the box is meant to take all of it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
6316bb1 parent: 5ba95e0 modified
crates/jolt-cosmic/src/lib.rs +36 -7 | @@ -32,6 +32,7 @@ use std::sync::{Arc, Condvar, LazyLock, Mutex, MutexGuard}; | ||
| 32 | 32 | use std::time::Duration; |
| 33 | 33 | |
| 34 | 34 | use cosmic::app::{Core, Task}; |
| 35 | +use cosmic::iced::alignment::Horizontal; | |
| 35 | 36 | use cosmic::iced::futures::channel::mpsc; |
| 36 | 37 | use cosmic::iced::futures::{Stream, StreamExt}; |
| 37 | 38 | use cosmic::iced::widget::container::Style as ContainerStyle; |
| @@ -857,9 +858,10 @@ fn width_request(n: &Node) -> Option<f32> { | ||
| 857 | 858 | n.num("width-request").filter(|w| *w > 0.0).map(|w| w as f32) |
| 858 | 859 | } |
| 859 | 860 | |
| 860 | -/// `align`, or `default` where it is not set. A row centres its children on | |
| 861 | -/// the cross axis by default — a label beside a button otherwise sits against | |
| 862 | -/// the top of the button — and a column starts them at the left. | |
| 861 | +/// `align`, or `default` where it is not set. A column starts its children at | |
| 862 | +/// the left. Rows do not ask: `align` on a row is where along the row its | |
| 863 | +/// children sit, not how they line up across it, and the row branch of | |
| 864 | +/// `element` reads it itself. | |
| 863 | 865 | fn alignment(n: &Node, default: Alignment) -> Alignment { |
| 864 | 866 | match n.str("align") { |
| 865 | 867 | "start" => Alignment::Start, |
| @@ -950,10 +952,25 @@ fn element(t: &Tree, id: i32, enabled: bool, in_row: bool) -> Element<'_, Messag | ||
| 950 | 952 | "box" => { |
| 951 | 953 | let across = n.str("orientation") == "horizontal"; |
| 952 | 954 | if across { |
| 953 | - let mut row = Row::with_children(children(true)) | |
| 954 | - .spacing(spacing) | |
| 955 | - .padding(margins(n)) | |
| 956 | - .align_y(alignment(n, Alignment::Center)); | |
| 955 | + // `align` on a row is the MAIN axis, which is glimmer's | |
| 956 | + // meaning and the one the shared screens are written against: | |
| 957 | + // `:end` lays the children out *from* the right, so the first | |
| 958 | + // child in the source is the rightmost on screen. Read as a | |
| 959 | + // cross-axis gravity instead it did nothing visible but sit | |
| 960 | + // the chips low, and the message heading's pair came out in | |
| 961 | + // the order ✏️ ↩️ 🙂 hard against the clock rather than the | |
| 962 | + // other way round against the edge — see `chat/action-chips`. | |
| 963 | + let from_end = n.str("align") == "end"; | |
| 964 | + let mut row = if from_end { | |
| 965 | + Row::with_children(children(true).collect::<Vec<_>>().into_iter().rev()) | |
| 966 | + } else { | |
| 967 | + Row::with_children(children(true)) | |
| 968 | + } | |
| 969 | + .spacing(spacing) | |
| 970 | + .padding(margins(n)) | |
| 971 | + // Across the row the children still centre: a chip beside a | |
| 972 | + // label sitting against the top of it is what that is for. | |
| 973 | + .align_y(Alignment::Center); | |
| 957 | 974 | // A row fills the width it is in only when it or something in |
| 958 | 975 | // it asks to; otherwise a line of buttons would spread out. |
| 959 | 976 | match width_request(n) { |
| @@ -964,6 +981,18 @@ fn element(t: &Tree, id: i32, enabled: bool, in_row: bool) -> Element<'_, Messag | ||
| 964 | 981 | if fill_height { |
| 965 | 982 | row = row.height(Length::Fill); |
| 966 | 983 | } |
| 984 | + if from_end { | |
| 985 | + // iced has no main-axis alignment on a Row, so the edge is | |
| 986 | + // a container's doing: it takes the width and puts the row | |
| 987 | + // against the right of it. Not a leading Fill space, which | |
| 988 | + // would have halved the slack with a row that already has | |
| 989 | + // something filling in it — the join box beside its button | |
| 990 | + // is that row, and the box is meant to take all of it. | |
| 991 | + return widget::container(row) | |
| 992 | + .width(Length::Fill) | |
| 993 | + .align_x(Horizontal::Right) | |
| 994 | + .into(); | |
| 995 | + } | |
| 967 | 996 | row.into() |
| 968 | 997 | } else { |
| 969 | 998 | let mut column = Column::with_children(children(false)) |
| @@ -32,6 +32,7 @@ use std::sync::{Arc, Condvar, LazyLock, Mutex, MutexGuard}; | |||
| 32 | use std::time::Duration; | 32 | use std::time::Duration; |
| 33 | 33 | ||
| 34 | use cosmic::app::{Core, Task}; | 34 | use cosmic::app::{Core, Task}; |
| 35 | +use cosmic::iced::alignment::Horizontal; | ||
| 35 | use cosmic::iced::futures::channel::mpsc; | 36 | use cosmic::iced::futures::channel::mpsc; |
| 36 | use cosmic::iced::futures::{Stream, StreamExt}; | 37 | use cosmic::iced::futures::{Stream, StreamExt}; |
| 37 | use cosmic::iced::widget::container::Style as ContainerStyle; | 38 | use cosmic::iced::widget::container::Style as ContainerStyle; |
| @@ -857,9 +858,10 @@ fn width_request(n: &Node) -> Option<f32> { | |||
| 857 | n.num("width-request").filter(|w| *w > 0.0).map(|w| w as f32) | 858 | n.num("width-request").filter(|w| *w > 0.0).map(|w| w as f32) |
| 858 | } | 859 | } |
| 859 | 860 | ||
| 860 | -/// `align`, or `default` where it is not set. A row centres its children on | 861 | +/// `align`, or `default` where it is not set. A column starts its children at |
| 861 | -/// the cross axis by default — a label beside a button otherwise sits against | 862 | +/// the left. Rows do not ask: `align` on a row is where along the row its |
| 862 | -/// the top of the button — and a column starts them at the left. | 863 | +/// children sit, not how they line up across it, and the row branch of |
| 864 | +/// `element` reads it itself. | ||
| 863 | fn alignment(n: &Node, default: Alignment) -> Alignment { | 865 | fn alignment(n: &Node, default: Alignment) -> Alignment { |
| 864 | match n.str("align") { | 866 | match n.str("align") { |
| 865 | "start" => Alignment::Start, | 867 | "start" => Alignment::Start, |
| @@ -950,10 +952,25 @@ fn element(t: &Tree, id: i32, enabled: bool, in_row: bool) -> Element<'_, Messag | |||
| 950 | "box" => { | 952 | "box" => { |
| 951 | let across = n.str("orientation") == "horizontal"; | 953 | let across = n.str("orientation") == "horizontal"; |
| 952 | if across { | 954 | if across { |
| 953 | - let mut row = Row::with_children(children(true)) | 955 | + // `align` on a row is the MAIN axis, which is glimmer's |
| 954 | - .spacing(spacing) | 956 | + // meaning and the one the shared screens are written against: |
| 955 | - .padding(margins(n)) | 957 | + // `:end` lays the children out *from* the right, so the first |
| 956 | - .align_y(alignment(n, Alignment::Center)); | 958 | + // child in the source is the rightmost on screen. Read as a |
| 959 | + // cross-axis gravity instead it did nothing visible but sit | ||
| 960 | + // the chips low, and the message heading's pair came out in | ||
| 961 | + // the order ✏️ ↩️ 🙂 hard against the clock rather than the | ||
| 962 | + // other way round against the edge — see `chat/action-chips`. | ||
| 963 | + let from_end = n.str("align") == "end"; | ||
| 964 | + let mut row = if from_end { | ||
| 965 | + Row::with_children(children(true).collect::<Vec<_>>().into_iter().rev()) | ||
| 966 | + } else { | ||
| 967 | + Row::with_children(children(true)) | ||
| 968 | + } | ||
| 969 | + .spacing(spacing) | ||
| 970 | + .padding(margins(n)) | ||
| 971 | + // Across the row the children still centre: a chip beside a | ||
| 972 | + // label sitting against the top of it is what that is for. | ||
| 973 | + .align_y(Alignment::Center); | ||
| 957 | // A row fills the width it is in only when it or something in | 974 | // A row fills the width it is in only when it or something in |
| 958 | // it asks to; otherwise a line of buttons would spread out. | 975 | // it asks to; otherwise a line of buttons would spread out. |
| 959 | match width_request(n) { | 976 | match width_request(n) { |
| @@ -964,6 +981,18 @@ fn element(t: &Tree, id: i32, enabled: bool, in_row: bool) -> Element<'_, Messag | |||
| 964 | if fill_height { | 981 | if fill_height { |
| 965 | row = row.height(Length::Fill); | 982 | row = row.height(Length::Fill); |
| 966 | } | 983 | } |
| 984 | + if from_end { | ||
| 985 | + // iced has no main-axis alignment on a Row, so the edge is | ||
| 986 | + // a container's doing: it takes the width and puts the row | ||
| 987 | + // against the right of it. Not a leading Fill space, which | ||
| 988 | + // would have halved the slack with a row that already has | ||
| 989 | + // something filling in it — the join box beside its button | ||
| 990 | + // is that row, and the box is meant to take all of it. | ||
| 991 | + return widget::container(row) | ||
| 992 | + .width(Length::Fill) | ||
| 993 | + .align_x(Horizontal::Right) | ||
| 994 | + .into(); | ||
| 995 | + } | ||
| 967 | row.into() | 996 | row.into() |
| 968 | } else { | 997 | } else { |
| 969 | let mut column = Column::with_children(children(false)) | 998 | let mut column = Column::with_children(children(false)) |