From c5c49b7789a102fcbeff4d6ecc07ef297e204dfb Mon Sep 17 00:00:00 2001 From: Roland Bengtsson Date: Wed, 16 Sep 2026 18:31:19 +0300 Subject: [PATCH 1/2] feat: add view mode buttons to the studio toolbar Add Editor only, Split view and Preview only buttons to the VCL studio toolbar. The three buttons share a GroupIndex and leave AllowAllUp false, so the VCL keeps exactly one of them pressed. ApplyViewMode now syncs the pressed button, so it also follows the other ways the mode can change: Ctrl+1/2/3, the command palette, session restore and leaving zen mode. Glyph and hint constants go in the shared defines unit next to the existing toolbar glyphs. The code points are present in both Segoe Fluent Icons and the Segoe MDL2 Assets fallback. --- .../Markdown4DStudioVCL.Defines.pas | 1 + .../Markdown4DStudioVCL.Main.pas | 46 +++++++++++++++++++ Examples/Shared/Markdown4DStudio.Defines.pas | 6 +++ 3 files changed, 53 insertions(+) diff --git a/Examples/Markdown4DStudioVCL/Markdown4DStudioVCL.Defines.pas b/Examples/Markdown4DStudioVCL/Markdown4DStudioVCL.Defines.pas index 2e3fdb2..3c39c9d 100644 --- a/Examples/Markdown4DStudioVCL/Markdown4DStudioVCL.Defines.pas +++ b/Examples/Markdown4DStudioVCL/Markdown4DStudioVCL.Defines.pas @@ -22,6 +22,7 @@ interface StatusLabelLeftMargin = 8; ButtonSpacing = 4; IconGlyphSize = 14; + ViewModeGroupIndex = 1; // TSpeedButton group for the mutually exclusive view mode buttons ToolbarLightColor = TColor($00F3F3F3); ToolbarDarkColor = TColor($002D2D2D); IconLightColor = TColor($00404040); diff --git a/Examples/Markdown4DStudioVCL/Markdown4DStudioVCL.Main.pas b/Examples/Markdown4DStudioVCL/Markdown4DStudioVCL.Main.pas index a6d90c2..52230df 100644 --- a/Examples/Markdown4DStudioVCL/Markdown4DStudioVCL.Main.pas +++ b/Examples/Markdown4DStudioVCL/Markdown4DStudioVCL.Main.pas @@ -87,6 +87,9 @@ TPadChrome = record FItalicButton: TSpeedButton; FLinkButton: TSpeedButton; FCodeButton: TSpeedButton; + FViewEditorButton: TSpeedButton; + FViewSplitButton: TSpeedButton; + FViewPreviewButton: TSpeedButton; FThemeButton: TSpeedButton; FTocButton: TSpeedButton; FZenButton: TSpeedButton; @@ -175,6 +178,10 @@ TPadChrome = record procedure HandleItalicClick(Sender: TObject); procedure HandleLinkClick(Sender: TObject); procedure HandleCodeClick(Sender: TObject); + procedure HandleViewEditorClick(Sender: TObject); + procedure HandleViewSplitClick(Sender: TObject); + procedure HandleViewPreviewClick(Sender: TObject); + procedure UpdateViewModeButtons; procedure HandleExportClick(Sender: TObject); procedure HandleCopyHtmlClick(Sender: TObject); procedure ExecuteFormatCommand(const Command: TEditorCommand); @@ -394,6 +401,16 @@ procedure TMarkdown4DStudioVCLForm.BuildToolbar; AddSeparator; + // The view mode buttons share a GroupIndex, so the VCL keeps exactly one of them + // down: clicking one releases the other two, and clicking the active one keeps it + // down because AllowAllUp stays False. + FViewEditorButton := AddIconButton(GlyphViewEditor, HintViewEditor, HandleViewEditorClick); + FViewEditorButton.GroupIndex := ViewModeGroupIndex; + FViewSplitButton := AddIconButton(GlyphViewSplit, HintViewSplit, HandleViewSplitClick); + FViewSplitButton.GroupIndex := ViewModeGroupIndex; + FViewPreviewButton := AddIconButton(GlyphViewPreview, HintViewPreview, HandleViewPreviewClick); + FViewPreviewButton.GroupIndex := ViewModeGroupIndex; + FThemeButton := AddIconButton(GlyphTheme, HintTheme, HandleThemeClick); FTocButton := AddIconButton(GlyphToc, HintToc, HandleTocClick); FZenButton := AddIconButton(GlyphZen, HintZen, HandleZenClick); @@ -757,6 +774,34 @@ procedure TMarkdown4DStudioVCLForm.HandleCodeClick(Sender: TObject); mdEditor.SetFocus; end; +procedure TMarkdown4DStudioVCLForm.HandleViewEditorClick(Sender: TObject); +begin + SetViewMode(TPadViewMode.EditorOnly); +end; + +procedure TMarkdown4DStudioVCLForm.HandleViewSplitClick(Sender: TObject); +begin + SetViewMode(TPadViewMode.Split); +end; + +procedure TMarkdown4DStudioVCLForm.HandleViewPreviewClick(Sender: TObject); +begin + SetViewMode(TPadViewMode.PreviewOnly); +end; + +procedure TMarkdown4DStudioVCLForm.UpdateViewModeButtons; +begin + // Keep the pressed button in step with the mode however it was set: toolbar click, + // Ctrl+1/2/3, the command palette, session restore or leaving zen mode. The nil + // guard covers ApplyViewMode running before BuildToolbar. + if FViewSplitButton = nil then + Exit; + + FViewEditorButton.Down := FViewMode = TPadViewMode.EditorOnly; + FViewSplitButton.Down := FViewMode = TPadViewMode.Split; + FViewPreviewButton.Down := FViewMode = TPadViewMode.PreviewOnly; +end; + procedure TMarkdown4DStudioVCLForm.HandleExportClick(Sender: TObject); begin DoExportHtml; @@ -1742,6 +1787,7 @@ procedure TMarkdown4DStudioVCLForm.ApplyViewMode; end; end; + UpdateViewModeButtons; EnforceTopBarOrder; EnforceLeftPaneOrder; end; diff --git a/Examples/Shared/Markdown4DStudio.Defines.pas b/Examples/Shared/Markdown4DStudio.Defines.pas index 6116a62..a9bdec0 100644 --- a/Examples/Shared/Markdown4DStudio.Defines.pas +++ b/Examples/Shared/Markdown4DStudio.Defines.pas @@ -23,6 +23,9 @@ interface GlyphItalic = Char($E8DB); GlyphLink = Char($E71B); GlyphCode = Char($E943); + GlyphViewEditor = Char($E70F); // Edit (pencil) + GlyphViewSplit = Char($E89A); // TwoPage + GlyphViewPreview = Char($E890); // View (eye) GlyphTheme = Char($E793); GlyphToc = Char($E8FD); GlyphFind = Char($E721); @@ -41,6 +44,9 @@ interface HintItalic = 'Italic (Ctrl+I)'; HintLink = 'Link (Ctrl+K)'; HintCode = 'Code block'; + HintViewEditor = 'Editor only (Ctrl+1)'; + HintViewSplit = 'Split view (Ctrl+2)'; + HintViewPreview = 'Preview only (Ctrl+3)'; HintTheme = 'Toggle theme'; HintToc = 'Toggle contents'; HintFind = 'Find in preview'; From 3350597e355d31f88dbbbee4dfcc059cd472aae9 Mon Sep 17 00:00:00 2001 From: Roland Bengtsson Date: Wed, 16 Sep 2026 18:49:32 +0300 Subject: [PATCH 2/2] feat: add view mode buttons to the FMX studio toolbar Bring the FMX studio to parity with the VCL one by adding Editor only, Split view and Preview only buttons to its toolbar. The FMX toolbar builds each button as a TRectangle with a child TText glyph, so there is no toggle control to lean on. The active mode is drawn as a filled background one step beyond the hover tone, through a new RestingColorFor helper that both the mouse leave handler and the theme switch consult. Without that the pressed look was cleared as soon as the pointer left the button or the theme changed. ApplyViewMode calls UpdateViewModeButtons, the same hook used in the VCL form, so the lit button follows Ctrl+1/2/3, the command palette, session restore and leaving zen mode as well as the toolbar itself. Left aligned FMX controls stack in reverse of their creation order, so the three are created preview first to read as editor, split, preview on screen and match the VCL layout. --- .../Markdown4DStudioFMX.Defines.pas | 4 ++ .../Markdown4DStudioFMX.Main.pas | 63 ++++++++++++++++++- 2 files changed, 65 insertions(+), 2 deletions(-) diff --git a/Examples/Markdown4DStudioFMX/Markdown4DStudioFMX.Defines.pas b/Examples/Markdown4DStudioFMX/Markdown4DStudioFMX.Defines.pas index 595bd67..4b42f2e 100644 --- a/Examples/Markdown4DStudioFMX/Markdown4DStudioFMX.Defines.pas +++ b/Examples/Markdown4DStudioFMX/Markdown4DStudioFMX.Defines.pas @@ -38,6 +38,10 @@ interface SeparatorDarkColor = TAlphaColor($FF505050); HoverLightColor = TAlphaColor($FFE0E0E0); HoverDarkColor = TAlphaColor($FF3E3E3E); + // A pressed toolbar button sits one step beyond hover, so the active view mode + // stays readable while the pointer rests on one of its neighbours. + ActiveLightColor = TAlphaColor($FFCBCBCB); + ActiveDarkColor = TAlphaColor($FF525252); TabActiveLightColor = TAlphaColor($FFFFFFFF); TabActiveDarkColor = TAlphaColor($FF3F3F3F); TabHoverLightColor = TAlphaColor($FFEAEAEA); diff --git a/Examples/Markdown4DStudioFMX/Markdown4DStudioFMX.Main.pas b/Examples/Markdown4DStudioFMX/Markdown4DStudioFMX.Main.pas index 19635cd..07b5ad6 100644 --- a/Examples/Markdown4DStudioFMX/Markdown4DStudioFMX.Main.pas +++ b/Examples/Markdown4DStudioFMX/Markdown4DStudioFMX.Main.pas @@ -72,6 +72,9 @@ TMarkdown4DStudioFMXForm = class(TForm, IPadEditorView, IPadShell) FItalicButton: TRectangle; FLinkButton: TRectangle; FCodeButton: TRectangle; + FViewEditorButton: TRectangle; + FViewSplitButton: TRectangle; + FViewPreviewButton: TRectangle; FThemeButton: TRectangle; FTocButton: TRectangle; FZenButton: TRectangle; @@ -83,6 +86,7 @@ TMarkdown4DStudioFMXForm = class(TForm, IPadEditorView, IPadShell) FSeparators: TArray; FToolbarFill: TAlphaColor; FHoverColor: TAlphaColor; + FActiveColor: TAlphaColor; FTocFill: TAlphaColor; FTocTextColor: TAlphaColor; FChromeTextColor: TAlphaColor; @@ -157,6 +161,8 @@ TMarkdown4DStudioFMXForm = class(TForm, IPadEditorView, IPadShell) procedure AddSeparator; procedure HandleIconMouseEnter(Sender: TObject); procedure HandleIconMouseLeave(Sender: TObject); + function RestingColorFor(const Button: TRectangle): TAlphaColor; + procedure UpdateViewModeButtons; procedure BuildTitleBar; function AddCaptionButton(const Glyph: string; const Hint: string; const Handler: TNotifyEvent): TRectangle; @@ -235,6 +241,9 @@ TMarkdown4DStudioFMXForm = class(TForm, IPadEditorView, IPadShell) procedure HandleItalicClick(Sender: TObject); procedure HandleLinkClick(Sender: TObject); procedure HandleCodeClick(Sender: TObject); + procedure HandleViewEditorClick(Sender: TObject); + procedure HandleViewSplitClick(Sender: TObject); + procedure HandleViewPreviewClick(Sender: TObject); procedure HandleThemeClick(Sender: TObject); procedure HandleTocClick(Sender: TObject); procedure HandleZenClick(Sender: TObject); @@ -406,6 +415,12 @@ procedure TMarkdown4DStudioFMXForm.BuildToolbar; FTocButton := AddIconButton(GlyphToc, HintToc, HandleTocClick); FThemeButton := AddIconButton(GlyphTheme, HintTheme, HandleThemeClick); + // Left aligned FMX controls stack in reverse of the order they are created, so + // these read as editor, split, preview on screen and match the VCL toolbar. + FViewPreviewButton := AddIconButton(GlyphViewPreview, HintViewPreview, HandleViewPreviewClick); + FViewSplitButton := AddIconButton(GlyphViewSplit, HintViewSplit, HandleViewSplitClick); + FViewEditorButton := AddIconButton(GlyphViewEditor, HintViewEditor, HandleViewEditorClick); + AddSeparator; FCodeButton := AddIconButton(GlyphCode, HintCode, HandleCodeClick); @@ -536,10 +551,35 @@ procedure TMarkdown4DStudioFMXForm.HandleIconMouseEnter(Sender: TObject); procedure TMarkdown4DStudioFMXForm.HandleIconMouseLeave(Sender: TObject); begin const Button = Sender as TRectangle; - Button.Fill.Color := FToolbarFill; + Button.Fill.Color := RestingColorFor(Button); HideHint; end; +function TMarkdown4DStudioFMXForm.RestingColorFor(const Button: TRectangle): TAlphaColor; +begin + // The view mode buttons carry a pressed state, so they do not fall back to the + // plain toolbar fill once the pointer leaves or the theme is swapped. + if ((Button = FViewEditorButton) and (FViewMode = TPadViewMode.EditorOnly)) or + ((Button = FViewSplitButton) and (FViewMode = TPadViewMode.Split)) or + ((Button = FViewPreviewButton) and (FViewMode = TPadViewMode.PreviewOnly)) then + Result := FActiveColor + else + Result := FToolbarFill; +end; + +procedure TMarkdown4DStudioFMXForm.UpdateViewModeButtons; +begin + // Keep the lit button in step with the mode however it was set: toolbar click, + // Ctrl+1/2/3, the command palette, session restore or leaving zen mode. The nil + // guard covers ApplyViewMode running before BuildToolbar. + if FViewSplitButton = nil then + Exit; + + FViewEditorButton.Fill.Color := RestingColorFor(FViewEditorButton); + FViewSplitButton.Fill.Color := RestingColorFor(FViewSplitButton); + FViewPreviewButton.Fill.Color := RestingColorFor(FViewPreviewButton); +end; + procedure TMarkdown4DStudioFMXForm.BuildTitleBar; begin FTitleBar := TRectangle.Create(Self); @@ -1212,6 +1252,8 @@ procedure TMarkdown4DStudioFMXForm.ApplyViewMode; else raise ENotSupportedException.CreateFmt('Unsupported view mode: %d', [Ord(FViewMode)]); end; + + UpdateViewModeButtons; end; procedure TMarkdown4DStudioFMXForm.ShowFindBar; @@ -1608,6 +1650,21 @@ procedure TMarkdown4DStudioFMXForm.HandleCodeClick(Sender: TObject); FEditor.SetFocus; end; +procedure TMarkdown4DStudioFMXForm.HandleViewEditorClick(Sender: TObject); +begin + SetViewMode(TPadViewMode.EditorOnly); +end; + +procedure TMarkdown4DStudioFMXForm.HandleViewSplitClick(Sender: TObject); +begin + SetViewMode(TPadViewMode.Split); +end; + +procedure TMarkdown4DStudioFMXForm.HandleViewPreviewClick(Sender: TObject); +begin + SetViewMode(TPadViewMode.PreviewOnly); +end; + procedure TMarkdown4DStudioFMXForm.HandleThemeClick(Sender: TObject); begin ToggleDarkTheme; @@ -1990,6 +2047,7 @@ procedure TMarkdown4DStudioFMXForm.ApplyTheme; FToolbarFill := ToolbarDarkColor; FHoverColor := HoverDarkColor; + FActiveColor := ActiveDarkColor; IconColor := IconDarkColor; SeparatorColor := SeparatorDarkColor; end @@ -2000,6 +2058,7 @@ procedure TMarkdown4DStudioFMXForm.ApplyTheme; FToolbarFill := ToolbarLightColor; FHoverColor := HoverLightColor; + FActiveColor := ActiveLightColor; end; FTocFill := FToolbarFill; @@ -2013,7 +2072,7 @@ procedure TMarkdown4DStudioFMXForm.ApplyTheme; for var Button in FIconButtons do begin - Button.Fill.Color := FToolbarFill; + Button.Fill.Color := RestingColorFor(Button); end; for var GlyphText in FIconGlyphs do