Repository navigation
Responsive SizedPanel: switch layouts when the wide one doesn't fit - #18
Merged
Merged
Conversation
SizedPanel(sizer_type=..., narrow_sizer_type=...) switches to the narrow sizer while the wide layout's measured minimum width (sizer CalcMin) does not fit the panel's client width, and back when it does. An optional narrow_below_chars threshold, in average character widths of the panel's font, replaces the measurement. The check runs on EVT_SIZE, after EVT_DPI_CHANGED, and from update_layout(), which apps call after changing fonts. Without narrow_sizer_type nothing changes. Switching goes through SetSizerType, which re-adds children in GetChildren() order, so Tab and screen reader order are unchanged. A "wrap" sizer type (wx.WrapSizer) lets a horizontal button row wrap when narrow. After a switch or a wrap re-flow the parent is re-laid out so the panel gets the height it now needs. Fields declared with hidden_when_narrow=True are hidden with hide() while narrow and shown again when wide, except fields the app had already hidden. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SeWnvjMTaHf6iZhDbaJpDE
…s hidden - A "horizontal" SizedPanel now wraps onto more lines (wx.WrapSizer) whenever its row does not fit, with no setting on each row. "wrap" is only the internal narrow layout for horizontal panels. - The wrap sizer is built with public Sizer calls: children are detached and re-added in GetChildren() order with their proportion, flag, border and user data, replacing the call to sized_controls' private _SetNewSizer. - GUIField.hide()/show() record the app's choice in hidden_by_app. The panel hides and shows hidden_when_narrow fields through the widget, so a field the app hides while the panel is narrow stays hidden when the panel goes wide again. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SeWnvjMTaHf6iZhDbaJpDE
Hiding or showing a field can lay out the panel's ancestors (as the label fix for #13 does), which resizes the panel in the middle of a switch. A re-entrant fit check then switched back halfway through, leaving a field hidden in the wide layout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SeWnvjMTaHf6iZhDbaJpDE
BaseForm.refresh() now calls update_layout() on every SizedPanel it walks, inner panels first, after re-applying labels and fonts, so a language or text-size change re-checks the layout. update_layout() now measures after the panel's next paint instead of immediately. GTK applies a new font in its next frame, and only to shown controls, so a label hidden by hidden_when_narrow kept its old size and the panel stayed narrow after a font decrease. The wide layout is shown first, size events are ignored until the measurement, and cached best sizes are cleared before it. Panels not on screen measure after pending events instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SeWnvjMTaHf6iZhDbaJpDE
On MSW (wx 4.3.2) a wrapped button row stayed one row tall: the parent gave the panel the height of one row, the second row got height 0 and dropped out of UIA. The parent reads the panel's best size, which comes from wxWrapSizer::CalcMin, whose result depends on the sizer's internal state (when it was last informed of a width, its last layout). Further frame resizes did not correct it, so the parent's view of the minimum was wrong, not a missing re-layout. While wrapped, the panel now informs the wrap sizer of its width, takes CalcMin right after (which depends only on the items and the width) and sets that height as its minimum size. GetEffectiveMinSize gives the minimum size priority over the best size, so the parent no longer depends on the wrap sizer's state. The app's declared min_size is restored when the panel leaves wrap mode. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SeWnvjMTaHf6iZhDbaJpDE
…on wxWidgets 3.3 In wxWidgets 3.3 (wxPython 4.3.2), wxSizer::InformFirstDirection is a deprecated stub that does nothing (include/wx/sizer.h), and wxWrapSizer implements CalcMinSizeFromKnownDirection instead. So CalcMin() after InformFirstDirection returned one row's height (81x29) and the wrapped second row was clipped to height 0 and missing from UIA on Windows. Use CalcMinSizeFromKnownDirection where it exists; keep InformFirstDirection + CalcMin for wxWidgets 3.2.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #14.
forms.SizedPanel(sizer_type="form", narrow_sizer_type="vertical")uses the wide layout while its measured minimum width fits the panel, and the narrow one otherwise. The optionalnarrow_below_charssets a fixed threshold in average character widths instead. Whennarrow_sizer_typeis omitted, behaviour is unchanged, except that a"horizontal"row wraps onto more lines (wx.WrapSizer) when it doesn't fit.EVT_SIZE, onEVT_DPI_CHANGED, fromupdate_layout(), and fromBaseForm.refresh()(Add form.refresh() to re-apply labels, titles, menus, tooltips, choices and fonts #17), which now re-checks every responsive panel after re-applying labels and fonts.hidden_when_narrow=Trueare hidden, labels included (Hide and show a field's label with the field #16), while the panel is narrow. A field the app hid itself is never re-shown.InformFirstDirectionis a no-op stub, so a wrapped row is measured withCalcMinSizeFromKnownDirection.Verified on Windows (wx 4.3.2 msw, private desktop, UIA via pywinauto). A frame with a form panel (Name, Email address, Phone and Notes with
hidden_when_narrow) and a horizontal row of four buttons, swept through widths 900 → 200 → 900:Not verified: a per-monitor DPI change, which can't be tested on this machine.