You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Keep the channel list aligned and prevent sidebar overlap #333
A long channel name can overflow its row. This can move its [X] remove button and break the alignment of the channel list.
The left sidebar resize handle can also move past its safe limit and enter the central chat area. The Disconnect button and all sidebar controls must stay inside the left sidebar.
Related issues: #257 and #290. The screenshots show that these layout problems can still occur.
Expected result
Keep all [X] buttons in one aligned column, including when a channel has a long name.
Set a minimum sidebar width that fits the longest channel button, its [X] button, and the required spacing.
Keep the gaps around the [X] buttons as small as the layout allows.
Stop the resize handle at the safe sidebar limits. It must not enter the chat area or cover a sidebar control.
Move the Disconnect button into the Home pane, beside the connection controls. Keep connection and disconnection in one place.
Check
Test short and long channel names.
Resize the sidebar in both directions.
Confirm that the channel controls, resize handle, and chat stay in their own areas.
Confirm that the Home pane contains both connection and disconnection controls.
This guide gives the facts and the steps to fix the issue for good. The facts come from the code at commit f9394449. Line numbers are for that commit and for crates/cordiale-ui/ui/appwindow.slint, unless a file name is given. Lines move, so search for the names.
What the screenshots show
Screenshot 1: all [X] buttons are in one column, except the one in the selected row ● #worldofmedia. That [X] moves right. The list clips it, and only a dark part stays visible.
Screenshot 2: the fifth top icon (Disconnect) is in the chat area, outside the sidebar. The first four icons are inside the sidebar.
Screenshot 2: the chat column looks narrower than the 240 px minimum in the code. The picture can be cropped at the right, so this point is not certain.
The network row and the "Channels" entry start at other left edges than the channel rows. This is by design (toggle button and indent). Do not change it.
Verified causes
The row label has no width limit. The label Button of a channel row and of a query row has horizontal-stretch: 1. It has no maximum width and no elision (lines 3263-3283 and 3316-3324). A long name, an emoji or the ● bullet makes the row wider. The cells after the label then move. The [X] buttons line up only while all labels fit.
Some rows get an extra 28 px cell. For an authoritative read count of zero, unread_message_count_labels (crates/cordiale-ui/src/main.rs, lines 7164-7170) returns an empty count and a non-empty description. The row condition tests the description (lines 3286 and 3326). Slint then adds an empty Text with min-width: 28px and 4 px spacing. That row is 32 px wider than the others. With the ● prefix, its [X] lands near x=203. The list clips it near x=211. This is screenshot 1.
The width probe is incomplete.widest_sidebar_label (main.rs, lines 9156-9189) builds the text for sidebar-label-probe. It includes the invite marker, the mention badge, the ↳ query prefix and an estimate for the network row. It does not include the ● bullet (lines 3267 and 3318) or the unread cell (lines 3286 and 3326). It uses the raw connection label, not the translated one. It picks the longest text by character count, not by pixel width. The fixed + 90px in sidebar-min-width (line 1326) covers the indent, the spacing, the [X] button, the scrollbar gutter and the box padding. It never counts the unread cell.
The icon row constant is wrong.sidebar-icons-min-width (line 1323) assumes five cells of 32 px, 4 px spacing and 12 px padding, which gives 200 px. The style box padding is 8 px. An icon button is about 44 px wide with an emoji glyph (this is inferred from the screenshot, where the icon pitch is about 48 px). The row has 4 or 5 icons, because if is-admin (line 3046) hides one.
Only the drag is limited. The limits sidebar-max-width and members-max-width (lines 1339-1344) are used only in the moved handler of ResizeHandle (lines 419-425). Nothing limits sidebar-width or members-width again when the window shrinks or when the probe grows. The real widths (lines 3032 and 4191) are max(stored, min) with no upper limit.
The window has no minimum width. The window sets only preferred-width: 900 and preferred-height: 600 (lines 911-912). The constants imply a minimum of about 640 px (200 + about 125 + 240 + 76). chat-min-width is used only inside the drag limits, not as a layout constraint. No changed width handler exists.
Make the row alignment true by construction. Replace the label Button of channel rows and query rows with a small local component. Use a Rectangle, a TouchArea and a Text with overflow: elide. Set min-width: 0px and horizontal-stretch: 1. The existing NavButton (lines 740-780) is a good model. Keep the accessible role, the accessible label, the default action, the keyboard focus, the primary colour and the opacity for failed and kicked rows. The label must never change the minimum width of the row.
Make the trailing cells constant. Use one fixed width for the unread cell and one fixed width (28 px) for the [X] cell. Reserve both cells in every row. Use an empty Rectangle when a cell has no content. Also test the count text (unread-count), not the description, so an authoritative zero does not create an empty cell.
Make the sidebar minimum correct. Build sidebar-min-width from named constants: indent 16, spacing 4, unread cell, [X] cell, scrollbar gutter and box padding. Remove the 90px magic number. Count the ● bullet and the unread cell in the probe text or in the constants. Never let the minimum exceed sidebar-max-width (360 px). After step 1, a name that is too long shows … and does not widen the sidebar.
Replace sidebar-icons-min-width with a measured value. Add a hidden probe row at the root, like sidebar-label-probe (lines 2615-2630). Put the same icon buttons in it, with the same is-admin condition. Read its preferred-width and add the real box padding. Also give TopMenuButton a real root size of 32 px by 32 px, and set the sidebar box padding to an explicit value. Then the arithmetic is true. The row has 3 or 4 icons after Disconnect leaves it.
Add a window minimum width. Set min-width to sidebar-min-width + members-min-width + chat-min-width + layout-fixed-width. These values depend on probes and constants, not on the window width, so no loop occurs. Also give the chat column and the directory view a real min-width: chat-min-width. Check that the minimum works on Windows, macOS and Linux.
Limit the stored widths again. Write one function fit-columns that does this: shrink the member column to its minimum first, then the sidebar to its minimum, and never go below a minimum. Call it from changed width, from changed sidebar-min-width, from changed members-min-width and after each drag. Keep the drag limit in ResizeHandle. Put the arithmetic in a pure function, and call it through a pure callback if that makes it simpler (see the Tests section).
Move Disconnect. Remove the sidebar button (lines 3073-3082) and its focus-name entries (lines 3087-3092). Add a card to the Home pane (the ScrollView at line 3577), after the Networks card (line 3735). Put a button in it that calls disconnect-requested(). Reuse the existing text "Disconnect". The Home pane already has a Disconnect button for each network (lines 3689-3696). That button disconnects one network, not the account. Give the new card a clear title, or add a short explanatory text, so the two do not look the same. A new title is a new user-visible string (see the translation rules below).
Do not
Do not read root.width in a width: binding of a column. This causes a binding loop (see history, point 7). Read it only in handlers, such as moved and changed width.
Do not fix the problem with a bigger magic number. Do not hide it with clipping.
Do not change what the disconnect-requested callback does. Other code uses it: the Actions menu "Switch account" (line 4280), Settings > Connection (line 4555), the /quit command (crates/cordiale-ui/src/slash.rs, line 409) and the session-severed path in main.rs. The handler is in crates/cordiale-ui/src/ui_callbacks.rs (lines 406-446).
Do not change the left edges of the network row and of the "Channels" entry.
Translation rules
A new or changed user-visible string needs @tr with the context AppWindow. Add the string to the four files crates/cordiale-ui/lang/{it,fr,de,es}/LC_MESSAGES/cordiale-ui.po. Use the informal form: tu (it), tu (fr), du (de), tú (es). Reuse the existing msgid "Disconnect" where possible.
Tests
The existing test widest_sidebar_label_picks_the_longest_row (crates/cordiale-ui/src/tests.rs, lines 4599-4624) checks the probe text. Update it if you change the probe text.
No Slint layout test or screenshot test exists in this repository. Add the column rule as a pure Rust function: requested width, minimum and maximum in, fitted width out. Add the fit-columns rule as a pure function too. Register them as pure callbacks in ui_callbacks.rs, as cleartext-key (line 970) and passkey-origin-valid (line 1535) do. Write unit tests in tests.rs. Cover the minimum, the maximum, a window that shrinks and a minimum above the maximum.
Run formatting, clippy with warnings as errors, and the tests before you open the pull request. The CI runs the same checks on Linux, Windows and macOS.
Write the result of the hand tests below in the pull request.
Hand tests
Use a short name, a 40-character name, a name with emoji or wide glyphs, and a name with a mention badge. Test selected and not selected rows, and channel, query, invited, failed and kicked rows. Every [X] button must be in one column. A long label must end with ….
Test a selected channel with an authoritative read count of zero, and a channel with no counts. No extra gap must appear.
Test unread counts of 0, 5 and 1234 on a long name. The badge must be visible, and the [X] button must stay aligned.
Test an admin account and a normal account. All icons must stay inside the sidebar at its minimum width. Test with font scale 150% and with each of the four languages.
Drag the sidebar handle to the minimum and to the maximum, with the member column at its minimum and at 320 px. The handle must never enter the chat or cover a control. Repeat for the member column handle.
Shrink the window after a wide drag, down to the window minimum. The columns must fit again. The chat must keep its minimum. Nothing must overlap. Test maximize, restore and snap to half of the screen.
Add a long channel while the window is at its minimum. The sidebar must not push the chat or the member column out of the window.
Show the invite banner, a file transfer offer and the recover banner with the narrowest sidebar. All buttons must stay inside the sidebar.
Open the Home pane. The account Disconnect button must be there. The Disconnect and Reconnect buttons of each network must still work. The new text must be translated. The old sidebar button must be gone. The keyboard focus order must be correct.
Open the channel directory (/list), open Settings and go back, and open the radio popup. The layout must stay stable.
Test Windows with 200% scaling, Linux and macOS, the dark and the light theme, and a theme with a different monospace font.
Done when
All [X] buttons stay in one column in every test above.
The sidebar, its handle and its controls stay inside the sidebar, at every window width down to the window minimum.
The Disconnect button is only in the Home pane, and all other callers of disconnect-requested still work.
The unit tests for the pure functions pass, and the CI is green on all systems.
The pull request lists the result of each hand test.
Activity
Sythos commentedon Oct 16, 2026
additiona screenshots:
Fix guide for the workers
This guide gives the facts and the steps to fix the issue for good. The facts come from the code at commit
f9394449. Line numbers are for that commit and forcrates/cordiale-ui/ui/appwindow.slint, unless a file name is given. Lines move, so search for the names.What the screenshots show
● #worldofmedia. That [X] moves right. The list clips it, and only a dark part stays visible.Verified causes
Buttonof a channel row and of a query row hashorizontal-stretch: 1. It has no maximum width and no elision (lines 3263-3283 and 3316-3324). A long name, an emoji or the●bullet makes the row wider. The cells after the label then move. The [X] buttons line up only while all labels fit.unread_message_count_labels(crates/cordiale-ui/src/main.rs, lines 7164-7170) returns an empty count and a non-empty description. The row condition tests the description (lines 3286 and 3326). Slint then adds an emptyTextwithmin-width: 28pxand 4 px spacing. That row is 32 px wider than the others. With the●prefix, its [X] lands near x=203. The list clips it near x=211. This is screenshot 1.widest_sidebar_label(main.rs, lines 9156-9189) builds the text forsidebar-label-probe. It includes the invite marker, the mention badge, the↳query prefix and an estimate for the network row. It does not include the●bullet (lines 3267 and 3318) or the unread cell (lines 3286 and 3326). It uses the raw connection label, not the translated one. It picks the longest text by character count, not by pixel width. The fixed+ 90pxinsidebar-min-width(line 1326) covers the indent, the spacing, the [X] button, the scrollbar gutter and the box padding. It never counts the unread cell.sidebar-icons-min-width(line 1323) assumes five cells of 32 px, 4 px spacing and 12 px padding, which gives 200 px. The style box padding is 8 px. An icon button is about 44 px wide with an emoji glyph (this is inferred from the screenshot, where the icon pitch is about 48 px). The row has 4 or 5 icons, becauseif is-admin(line 3046) hides one.sidebar-max-widthandmembers-max-width(lines 1339-1344) are used only in themovedhandler ofResizeHandle(lines 419-425). Nothing limitssidebar-widthormembers-widthagain when the window shrinks or when the probe grows. The real widths (lines 3032 and 4191) aremax(stored, min)with no upper limit.preferred-width: 900andpreferred-height: 600(lines 911-912). The constants imply a minimum of about 640 px (200 + about 125 + 240 + 76).chat-min-widthis used only inside the drag limits, not as a layout constraint. Nochanged widthhandler exists.Required fix
Do all steps in one pull request.
Buttonof channel rows and query rows with a small local component. Use aRectangle, aTouchAreaand aTextwithoverflow: elide. Setmin-width: 0pxandhorizontal-stretch: 1. The existingNavButton(lines 740-780) is a good model. Keep the accessible role, the accessible label, the default action, the keyboard focus, the primary colour and the opacity for failed and kicked rows. The label must never change the minimum width of the row.Rectanglewhen a cell has no content. Also test the count text (unread-count), not the description, so an authoritative zero does not create an empty cell.sidebar-min-widthfrom named constants: indent 16, spacing 4, unread cell, [X] cell, scrollbar gutter and box padding. Remove the90pxmagic number. Count the●bullet and the unread cell in the probe text or in the constants. Never let the minimum exceedsidebar-max-width(360 px). After step 1, a name that is too long shows…and does not widen the sidebar.sidebar-icons-min-widthwith a measured value. Add a hidden probe row at the root, likesidebar-label-probe(lines 2615-2630). Put the same icon buttons in it, with the sameis-admincondition. Read itspreferred-widthand add the real box padding. Also giveTopMenuButtona real root size of 32 px by 32 px, and set the sidebar box padding to an explicit value. Then the arithmetic is true. The row has 3 or 4 icons after Disconnect leaves it.min-widthtosidebar-min-width + members-min-width + chat-min-width + layout-fixed-width. These values depend on probes and constants, not on the window width, so no loop occurs. Also give the chat column and the directory view a realmin-width: chat-min-width. Check that the minimum works on Windows, macOS and Linux.fit-columnsthat does this: shrink the member column to its minimum first, then the sidebar to its minimum, and never go below a minimum. Call it fromchanged width, fromchanged sidebar-min-width, fromchanged members-min-widthand after each drag. Keep the drag limit inResizeHandle. Put the arithmetic in a pure function, and call it through a pure callback if that makes it simpler (see the Tests section).ScrollViewat line 3577), after the Networks card (line 3735). Put a button in it that callsdisconnect-requested(). Reuse the existing text "Disconnect". The Home pane already has a Disconnect button for each network (lines 3689-3696). That button disconnects one network, not the account. Give the new card a clear title, or add a short explanatory text, so the two do not look the same. A new title is a new user-visible string (see the translation rules below).Do not
root.widthin awidth:binding of a column. This causes a binding loop (see history, point 7). Read it only in handlers, such asmovedandchanged width.disconnect-requestedcallback does. Other code uses it: the Actions menu "Switch account" (line 4280), Settings > Connection (line 4555), the/quitcommand (crates/cordiale-ui/src/slash.rs, line 409) and the session-severed path inmain.rs. The handler is incrates/cordiale-ui/src/ui_callbacks.rs(lines 406-446).Translation rules
A new or changed user-visible string needs
@trwith the contextAppWindow. Add the string to the four filescrates/cordiale-ui/lang/{it,fr,de,es}/LC_MESSAGES/cordiale-ui.po. Use the informal form: tu (it), tu (fr), du (de), tú (es). Reuse the existing msgid "Disconnect" where possible.Tests
widest_sidebar_label_picks_the_longest_row(crates/cordiale-ui/src/tests.rs, lines 4599-4624) checks the probe text. Update it if you change the probe text.fit-columnsrule as a pure function too. Register them as pure callbacks inui_callbacks.rs, ascleartext-key(line 970) andpasskey-origin-valid(line 1535) do. Write unit tests intests.rs. Cover the minimum, the maximum, a window that shrinks and a minimum above the maximum.Hand tests
…./list), open Settings and go back, and open the radio popup. The layout must stay stable.Done when
disconnect-requestedstill work.