Configurable Default Save Format (ODF / OOXML) - #36
Open
pplupo wants to merge 34 commits into
Open
Conversation
Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Remove redundant QT_ROOT override. Note: there is still a bug with characters with stress marks, such as ã é ü, etc. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
qcefview.h now includes QOpenGLWidget for the Wayland OSR presenter, and this target moc's that header directly, so it needs the OpenGLWidgets component too or automoc fails with "QOpenGLWidget: No such file". Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
mapToGlobal() relies on knowing the widget's absolute screen position, which Wayland deliberately doesn't expose to clients -- it was returning a bogus point, and QMenu fell back to centering itself. QContextMenuEvent's own global position is computed correctly by Qt's input pipeline at the time the event was generated, so use that instead of mapping the local position after the fact. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
…on() in this Qt version Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
… position CMenuWidget is a custom QWidget-based popup using Qt::Tool, which under Wayland maps to a plain xdg_toplevel with set_parent() -- there's no positioner data, so the compositor defaults to centering it over its parent regardless of any move() call, explaining why it rendered dead center of the main window rather than at the click position (a previous attempt to fix the position passed to exec() had no effect, confirming the problem isn't the position value itself). Add Qt::Popup so this maps to the xdg_popup protocol instead, which does support anchored positioning. Dismiss-on-outside-click/Escape is already handled explicitly in this class's own eventFilter (WindowDeactivate/MouseButtonPress/KeyRelease), so Qt::Popup's implicit grab/dismiss behavior is redundant with that, not conflicting. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Qt defines Tool = Popup | Dialog, so the previous "Qt::Tool | Qt::Popup" change didn't actually change anything -- the Popup bit was already part of Qt::Tool's value, so the resolved window type was still exactly Tool (confirmed: menu still centered on the window after that build). Use Qt::Popup alone so the widget's actual resolved window type changes, which is what Qt's Wayland QPA plugin needs to map it to the xdg_popup protocol instead of a plain xdg_toplevel. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Signed-off-by: Hendrik Leidinger <hendrik.leidinger@nextcloud.com> Assisted-by: Claude Code:Opus 4.8
Signed-off-by: Hendrik Leidinger <hendrik.leidinger@nextcloud.com> Assisted-by: Claude Code:Opus 4.8
Signed-off-by: Hendrik Leidinger <hendrik.leidinger@nextcloud.com>
Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
getScreenDpiRatio(), getScreenDpiRatioByHWND(), and getScreenDpiRatioByWidget() each hardcoded a flat 1.0 on Wayland, reasoning that Qt's automatic HiDPI backing-store scaling makes manual scaling redundant there. That conflates two different things: Qt's automatic scaling only controls how many physical pixels render a given logical (DIP) size -- it doesn't retroactively enlarge a DIP size an app explicitly computed from a hardcoded constant, e.g. cplatformdecoration.cpp's CUSTOM_BORDER_WIDTH * dpi_ratio, which sizes the native window decoration/caption region containing the tab strip. That manual computation needs a real ratio on Wayland exactly like it already does on X11, so let all three fall through to the same QDpiChecker-based path X11 already uses (now Wayland-aware after the desktop-sdk fix), instead of X11 || Wayland branching. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
CWindowBase's constructor, startRect(), and setScreenScalingFactor(), plus the setScreenScalingFactor() overrides in CWindowPlatform, CPresenterWindow, CEditorWindow, and CMainWindow all independently hardcoded factor/dpi to 1.0 on Wayland before it ever reached CPlatformDecoration's border sizing or m_boxTitleBtns (title bar/tab button) sizing -- silently discarding whatever real scale Utils::getScreenDpiRatio()/getScreenDpiRatioByWidget() computed (already fixed to be Wayland-aware) and leaving the tab strip sized as if unscaled regardless of the actual display scale. Same root pattern as the earlier DPI-checker fix: each of these assumed Qt's own automatic widget scaling made this redundant on Wayland, but that only covers backing-store render resolution, not layout sizes computed explicitly from a hardcoded base constant times a ratio. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
…gantic Wayland widgets Native widgets (CWindowBase, CMessage, etc.) call this once at construction time with no debounce, unlike CEF's UpdateUIScalePercentage() which now guards against the known startup devicePixelRatio() transient-misread race. Need to confirm whether choose_scaling() is snapping such a misread to a too-high standard step for these widgets, and whether it recurs for widgets constructed well after startup (on-demand dialogs), which would rule out a pure startup-race explanation. Remove once the underlying issue is found. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Extends the earlier debug logging to compare QWidget::devicePixelRatio() against QScreen::devicePixelRatio() for the same widget, to confirm they diverge on Wayland (screen-level appears stuck at the initial integer-rounded buffer scale) before relying on that as the basis for the desktop-sdk fix switching QDpiChecker::GetWidgetDpi() to the widget-level value. Remove once confirmed. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Qt's Wayland platform plugin disables fractional-scale support and permanently rounds devicePixelRatio() to the nearest integer for ordinary widgets (e.g. a real 1.25 scale reads as 2) unless the app explicitly opts into PassThrough -- confirmed as a known, documented Qt/Wayland behavior, not a startup timing race as earlier commits in this area assumed. This is the actual root cause of native widgets (window decoration, title-bar buttons, dialogs) rendering oversized: Utils::getScreenDpiRatioByWidget() was correctly reading devicePixelRatio(), but Qt itself was permanently misreporting it. Also removes the temporary debug logging added while investigating this, now that the underlying cause is confirmed. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Even with HighDpiScaleFactorRoundingPolicy::PassThrough set, initial sizing on Wayland can still be computed from a transiently-wrong devicePixelRatio() reading -- the same startup compositor round-trip race already proven for CEF's UI scale, just with no debounce here since these widgets read the value once, synchronously, at construction. Add Utils::WatchForDpiChange(), a reusable helper installing an event filter for QEvent::DevicePixelRatioChange (fired per-widget whenever Qt's own notion of that widget's effective ratio changes, for any reason -- the async correction arriving, or a real later change like moving to a different-scaled monitor). Wire it into CWindowBase's constructor to re-run updateScaling() when it fires, covering the main window and its subclasses (title bar, buttons, tabs). CMessage (dialogs) is not wired up yet -- its DPI-dependent sizing is spread inline across its constructor rather than a separate, re-callable method, so doing the same there needs a small refactor first rather than being folded into this change. Also re-adds (and extends, with a timestamp for correlating against the CEF debug log) the temporary debug logging removed earlier, now that PassThrough alone didn't fully resolve the issue. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Dead code with no remaining callers; had a stale Wayland hardcode (return 1.0) inconsistent with the widget-level DPI fix applied elsewhere. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Investigation is concluded; the underlying fix is confirmed working. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
QtMsg (CMessage's underlying dialog) read its own devicePixelRatio() once at construction time, before Qt had received the Wayland compositor's async fractional-scale answer for the dialog's surface -- the same startup race already fixed for CWindowBase, but this dialog was never wired into the reactive correction. Extracts the dpiRatio-dependent sizing into applyScaling(), called once at construction and again via WatchForDpiChange whenever the dialog's own devicePixelRatio() settles to its real value. Signed-off-by: Peter P. Lupo <pplupo@gmail.com> (cherry picked from commit 4273100)
QLayout::SetFixedSize locks the dialog's top-level size the first time it activates. Shrinking a child's minimum width afterwards (the WatchForDpiChange-triggered re-run, once the real ratio arrives) doesn't automatically renegotiate an already-mapped Wayland surface down to the smaller size, leaving the outer window oversized even though its contents are now correctly scaled. Force the resize explicitly via layout()->invalidate() + adjustSize(). Signed-off-by: Peter P. Lupo <pplupo@gmail.com> (cherry picked from commit 4dff132)
The app scales native Qt widgets manually (sizes/margins x dpiRatio, plus a QSS 'scaling' property that bakes the factor into pixel font-sizes) as a substitute for Qt's automatic HiDPI scaling, which is deliberately disabled on X11 so devicePixelRatio() stays 1. On Wayland, HighDpiScaleFactorRoundingPolicy::PassThrough makes Qt's devicePixelRatio report the true fractional scale and Qt already scales every widget by it. The manual scaling then applies on top, so a 1.25 display rendered native UI at ~1.56x. This was uniform across the whole native UI (only visible by comparing a dialog side-by-side with X11, since a uniform enlargement has no reference). Make Qt's PassThrough devicePixelRatio the single source of scaling on Wayland: getScreenDpiRatioByWidget/getScreenDpiRatioByHWND now return a neutral 1.0 there, so the manual factor is 1.0 and Qt does the scaling once, crisply, matching X11's physical result. Since the Wayland factor no longer depends on the late-arriving devicePixelRatio, the startup race it chased is gone: drop QtMsg's WatchForDpiChange re-derivation and the forced-resize workaround. Signed-off-by: Peter P. Lupo <pplupo@gmail.com> (cherry picked from commit 3ed1c04)
Open
1 task
Captures platform, widget class, size, widget/screen devicePixelRatio, and the computed manual scaling factor for every native widget, so the oversized dialog's numbers can be compared against the correctly-sized main window on Wayland. To be reverted once diagnosed. Signed-off-by: Peter P. Lupo <pplupo@gmail.com> (cherry picked from commit d99f644)
The dialog computes its scaling factor once from its (stable) parent in QtMsgPrivateIntf and applies it in the constructor -- which is already correct (1.25) and matches how X11 styles the dialog exactly once. The WatchForDpiChange re-derivation added earlier read the dialog's OWN devicePixelRatio, which on Wayland races 2.0 -> 1.25 as the compositor answers for the freshly-created surface. That fired applyScaling() with a transient 2.0 (scaling="2x": double fonts/margins) and then 1.25, leaving the dialog oversized, with the wrong icon, non-bold and non-centered text, and a massive height. X11 never sees the race (its devicePixelRatio never changes, so the watcher never fired) and looked correct. Drop the watcher and the forced-resize workaround so Wayland styles once from the parent ratio too. Also removes the temporary per-widget DPI diagnostic logging. Signed-off-by: Peter P. Lupo <pplupo@gmail.com> (cherry picked from commit e155cbf)
Size (root cause): QtMsg computed its scale from getScreenDpiRatioByWidget on the DIALOG itself. A freshly-created top-level surface reports the rounded default devicePixelRatio (2.0) on Wayland until the compositor answers with the real fractional scale, so the dialog was sized for 2.0 (scaling=2x, everything doubled) and never corrected -- hence 'enormous'. Derive the ratio from the stable parent window instead (its dpr has long since settled), via stableDpiRatioForDialog(). Styling: on Wayland the app uses this custom QtMsg (X11 uses the native GTK dialog); QtMsg was a plain fallback that never matched GTK. Bring it in line with the GTK message dialog: bold the primary (first) line and keep following lines regular, centre the text, and recolour the warning icon from yellow (#f1c40f) to GTK's warning orange (#f57900). Signed-off-by: Peter P. Lupo <pplupo@gmail.com> (cherry picked from commit b7fab27)
- Enlarge the primary line (bold + ~1.2x the secondary font size), split cleanly from the secondary line which stays regular. - Widen the gap between the primary and secondary lines. - Enlarge the message icon (35 -> 44) closer to GTK's dialog icon size. Signed-off-by: Peter P. Lupo <pplupo@gmail.com> (cherry picked from commit 768387d)
…lign text setMinimumWidth was multiplied by dpiRatio (e.g. 1.25), making the dialog 25% wider than the X11/GTK equivalent and forcing text centering to leave a large dead gap between the icon and the primary line. Use unscaled logical-pixel values for the minimum width (Qt's layout engine already handles DPI independently) and change label alignment to AlignLeft so the text sits flush in its column, matching the GTK message dialog layout. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Wrap the content row (icon + text) and the buttons row together in a single bodyWidget. Add it to cLayout with AlignCenter so the whole block uses its natural width and is centered symmetrically. Previously the content row filled the entire available width while m_boxButtons was AlignCenter — so the icon's left edge was at the far left margin while the buttons started further in, creating an unbalanced appearance. Now both rows share the same horizontal extent. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
New documents are named and saved as OOXML by default, with the format resolved at creation time from a tiered configuration cascade built on the existing QSettings registry group -- the same one that already governs CheckForUpdates -- rather than a new configuration subsystem. Resolution order (Utils::defaultSaveFormat): 1. system scope EnforceDefaultFormat=true -> system DefaultSaveFormat, stop 2. user scope DefaultSaveFormat, if set 3. system scope DefaultSaveFormat, if set 4. compiled-in baseline, OOXML Two keys are added to the existing group: DefaultSaveFormat (ODF|OOXML) and EnforceDefaultFormat. On Linux the files are /etc/xdg/euro-office/ DesktopEditors.conf (system) and ~/.config/euro-office/DesktopEditors.conf (user); on Windows the corresponding HKLM/HKCU keys. Note the group is REG_GROUP_KEY, i.e. lowercase "euro-office" on Linux. The user scope is read with fallbacks disabled: QSettings falls back to the system scope by default, which would otherwise make an administrator's baseline indistinguishable from a preference the user set for themselves, breaking both tier 2 vs. tier 3 precedence and the onboarding trigger. Surfaces: - Start page Settings gains a "Default save format" combo, carried over the existing settings:init / settings:apply round-trip. When the format is enforced the combo is disabled and the panel explains that the setting is managed by the organization; an enforced format is also ignored server-side in applySettings, so policy cannot be bypassed from the renderer. - A one-time startup dialog lets an unmanaged user pick a format before creating their first document. It is skipped when an administrator has set or locked a format, or once the user has chosen. Dismissing it without choosing records FormatOnboardingShown so it never reappears, and resolution falls through as normal. newFileFormat() centralizes the document-type -> format mapping that was duplicated across the create:new handler, the --new: command-line path and newFileName(); forms stay DOCXF since they are only defined for OOXML. The cascade was verified against real QSettings files across 15 cases covering all four tiers, the hard lock, malformed values and the dismissed-onboarding path. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
Separates the closing 'You can change this anytime in Settings.' line from the ODF description paragraph above it. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
The start page's document-creation tile grid hardcoded DOCX/XLSX/PPTX badge text and never reflected the resolved DefaultSaveFormat, so a user who set ODF as their default still saw OOXML extensions on the tiles they'd click to create a new document. The PDF/form tile is unaffected -- forms always stay DOCXF/PDF regardless of this setting, matching newFileFormat()'s existing behavior. The underlying "-big" icon glyphs (common-svg/docx-big.svg etc.) have no format-specific detail baked in -- plain page/grid/slide shapes -- so they're reused as-is for ODT/ODS/ODP; only the badge text changes. panelrecent.js now reads the resolved format from two places: directly from settings:init (registered at genuine app startup, independent of whether the Settings panel has ever been opened -- its own settings:init parsing only activates once that panel is constructed), and from a new defaultformat:changed event fired by panelsettings.js both at its own settings:init parse and right after a successful Settings-Apply, so the tiles update immediately without waiting for a fresh native round-trip. DocumentCreationGrid.render() now clears its own previous output before appending, so it can be safely re-rendered with a new format each time the resolved default changes. Signed-off-by: Peter P. Lupo <pplupo@gmail.com>
pplupo
force-pushed
the
feature/default-save-format
branch
from
July 31, 2026 03:45
35b216a to
cd7eacb
Compare
25 tasks
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.
Implementation of Euro-Office/DesktopEditors#60
Summary
CheckForUpdates.DefaultSaveFormat(ODF|OOXML) andEnforceDefaultFormat, readable via standard OS deployment tooling (/etc/xdg/euro-office/DesktopEditors.confon Linux,HKLM/HKCUon Windows) with no new config subsystem.settings:init/settings:applyround-trip; disabled with an explanatory message when an administrator has locked the format, and the lock is enforced server-side (not just hidden in the UI), so it can't be bypassed from the renderer.Test plan
QSettingsfiles across 15 cases: all four tiers, the hard lock, malformed/lowercase values, and the dismissed-onboarding pathcreate:newand--new:CLI pathssettings:apply→ registry write → next new document reflects it, in both directions (ODF→OOXML→ODF)settings:applycalls bypassing the disabled UI are rejected server-sideATTENTION: This is to go on top of the wayland-migration branch.