diff --git a/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/HelpMenu.tsx b/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/HelpMenu.tsx new file mode 100644 index 000000000000..53bbfca3d6f2 --- /dev/null +++ b/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/HelpMenu.tsx @@ -0,0 +1,39 @@ +import * as React from "react"; +import { BloomTooltip } from "../../BloomToolTip"; +import { ArrowDropDown, HelpOutline } from "@mui/icons-material"; +import { postJson, useApiString } from "../../../utils/bloomApi"; +import { TopRightMenuButton, topRightMenuArrowCss } from "./TopRightMenuButton"; +import { useL10n } from "../../l10nHooks"; + +export const HelpMenu: React.FunctionComponent = () => { + const helpText = useL10n("?", "HelpMenu.Help Menu"); + + const uiLanguage = useApiString("currentUiLanguage", "en"); + + const showIconOnly = + helpText === "?" || ["en", "fr", "de", "es"].includes(uiLanguage); + + const onOpen = () => { + postJson("workspace/topRight/openHelpMenu", {}); + }; + + const button = ( + : undefined} + endIcon={} + hasText={!showIconOnly} + /> + ); + + if (!showIconOnly) { + return button; + } + + return ( + + {button} + + ); +}; diff --git a/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/TopRightMenuButton.tsx b/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/TopRightMenuButton.tsx new file mode 100644 index 000000000000..b8d1039e4312 --- /dev/null +++ b/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/TopRightMenuButton.tsx @@ -0,0 +1,44 @@ +import { css } from "@emotion/react"; +import * as React from "react"; +import BloomButton from "../../bloomButton"; + +interface TopRightMenuButtonProps { + text: string; + onClick: () => void; + startIcon?: React.ReactNode; + endIcon?: React.ReactNode; + hasText?: boolean; +} + +export const topRightMenuArrowCss = css` + font-size: 14px !important; +`; + +export const TopRightMenuButton: React.FunctionComponent< + TopRightMenuButtonProps +> = (props) => { + return ( + + {props.text} + + ); +}; diff --git a/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/UiLanguageMenu.tsx b/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/UiLanguageMenu.tsx new file mode 100644 index 000000000000..0d8e66f3a68e --- /dev/null +++ b/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/UiLanguageMenu.tsx @@ -0,0 +1,20 @@ +import * as React from "react"; +import { ArrowDropDown } from "@mui/icons-material"; +import { postJson, useApiString } from "../../../utils/bloomApi"; +import { TopRightMenuButton, topRightMenuArrowCss } from "./TopRightMenuButton"; + +export const UiLanguageMenu: React.FunctionComponent = () => { + const label = useApiString("workspace/topRight/uiLanguageLabel", ""); + + const onOpen = () => { + postJson("workspace/topRight/openLanguageMenu", {}); + }; + + return ( + } + /> + ); +}; diff --git a/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/WorkspaceTopRightControls.tsx b/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/WorkspaceTopRightControls.tsx new file mode 100644 index 000000000000..a03162ab5afe --- /dev/null +++ b/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/WorkspaceTopRightControls.tsx @@ -0,0 +1,86 @@ +import { css } from "@emotion/react"; +import * as React from "react"; +import { useState } from "react"; +import { lightTheme } from "../../../bloomMaterialUITheme"; +import { createTheme, ThemeProvider } from "@mui/material/styles"; +import { WireUpForWinforms } from "../../../utils/WireUpWinform"; +import { CssBaseline } from "@mui/material"; +import { ZoomControl } from "./ZoomControl"; +import { UiLanguageMenu } from "./UiLanguageMenu"; +import { HelpMenu } from "./HelpMenu"; +import { kTextOnPurple } from "../../../bloomMaterialUITheme"; +import { useSubscribeToWebSocketForStringMessage } from "../../../utils/WebSocketManager"; + +export const WorkspaceTopRightControls: React.FunctionComponent = () => { + const lightThemeOverride = React.useMemo( + () => + createTheme(lightTheme, { + components: { + // kTextOnPurple: The background isn't always purple, + // but this matches what the original winforms control was doing. + // Without the override, we get Bloom blue. + MuiButton: { + styleOverrides: { + root: { + color: kTextOnPurple, + fontWeight: "normal", + }, + text: { + color: kTextOnPurple, + fontWeight: "normal", + }, + }, + }, + }, + }), + [], + ); + + // Forces a refresh. Currently used for localization changes. + const [generation, setGeneration] = useState(0); + useSubscribeToWebSocketForStringMessage("app", "uiLanguageChanged", () => { + setGeneration((current) => current + 1); + }); + + return ( + + {/* CssBaseline injects MUI's base styles (it sets html/body to the theme typography, + normalizes margins, etc.). Without it, the browser keeps default fonts and spacing, + so our theme's font family/size and resets never reach this control. */} + + + {/* This grid keeps the two menu buttons the same width which keeps the down arrows aligned horizontally */} + + + + + + + + ); +}; + +WireUpForWinforms(WorkspaceTopRightControls); diff --git a/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/ZoomControl.tsx b/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/ZoomControl.tsx new file mode 100644 index 000000000000..f4c97dabdfa1 --- /dev/null +++ b/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/ZoomControl.tsx @@ -0,0 +1,96 @@ +import { css } from "@emotion/react"; +import * as React from "react"; +import { useEffect, useState } from "react"; +import BloomButton from "../../bloomButton"; +import { get, postJson } from "../../../utils/bloomApi"; +import { useSubscribeToWebSocketForObject } from "../../../utils/WebSocketManager"; + +interface IZoomInfo { + zoom: number; + minZoom: number; + maxZoom: number; + zoomEnabled: boolean; +} + +export const ZoomControl: React.FunctionComponent = () => { + const [zoomInfo, setZoomInfo] = useState(undefined); + + // Fetch zoom info once on initial load. + useEffect(() => { + get("workspace/topRight/zoom", (result) => { + const zoomInfo = result.data as IZoomInfo; + setZoomInfo(zoomInfo); + }); + }, []); + // Get subsequent updates via WebSocket. + useSubscribeToWebSocketForObject( + "workspaceTopRightControls", + "zoom", + setZoomInfo, + ); + + const clampZoom = (value: number, current: IZoomInfo) => { + return Math.min(Math.max(value, current.minZoom), current.maxZoom); + }; + + const applyDelta = (delta: number) => { + if (!zoomInfo) { + return; + } + const clamped = clampZoom(zoomInfo.zoom + delta, zoomInfo); + setZoomInfo({ ...zoomInfo, zoom: clamped }); + postJson("workspace/topRight/zoom", { zoom: clamped }); + }; + + if (!zoomInfo || !zoomInfo.zoomEnabled) { + return null; + } + + return ( + + applyDelta(-10)} /> + + {`${zoomInfo.zoom}%`} + + applyDelta(10)} /> + + ); +}; + +interface PlusMinusButtonProps { + label: string; + onClick: () => void; +} + +const PlusMinusButton: React.FunctionComponent = ( + props, +) => { + return ( + + {props.label} + + ); +}; diff --git a/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/workspaceTopRightControls.entry.tsx b/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/workspaceTopRightControls.entry.tsx new file mode 100644 index 000000000000..51eacdf09c7c --- /dev/null +++ b/src/BloomBrowserUI/react_components/TopBar/workspaceTopRightControls/workspaceTopRightControls.entry.tsx @@ -0,0 +1,4 @@ +import { bootstrapReactComponent } from "../../../utils/entryPointBootstrap"; +import { WorkspaceTopRightControls } from "./WorkspaceTopRightControls"; + +bootstrapReactComponent(WorkspaceTopRightControls); diff --git a/src/BloomBrowserUI/vite.config.mts b/src/BloomBrowserUI/vite.config.mts index dac69aa98036..19eece4f357e 100644 --- a/src/BloomBrowserUI/vite.config.mts +++ b/src/BloomBrowserUI/vite.config.mts @@ -555,6 +555,8 @@ export default defineConfig(async ({ command }) => { editTopBarControlsBundle: "./bookEdit/topbar/editTopBarControls.tsx", collectionTopBarControlsBundle: "./react_components/TopBar/CollectionTopBarControls/CollectionTopBarControls.tsx", + workspaceTopRightControlsBundle: + "./react_components/TopBar/workspaceTopRightControls/workspaceTopRightControls.entry.tsx", }; // MAIN VITE CONFIGURATION diff --git a/src/BloomExe/CollectionChoosing/OpenCreateCloneControl.cs b/src/BloomExe/CollectionChoosing/OpenCreateCloneControl.cs index 29dbe9f951d1..18a036e8f44d 100644 --- a/src/BloomExe/CollectionChoosing/OpenCreateCloneControl.cs +++ b/src/BloomExe/CollectionChoosing/OpenCreateCloneControl.cs @@ -342,10 +342,7 @@ public void UpdateUiLanguageMenuSelection() if (tag.LangTag == Settings.Default.UserInterfaceLanguage) { item.Select(); - WorkspaceView.UpdateMenuTextToShorterNameOfSelection( - _uiLanguageMenu, - item.Text - ); + _uiLanguageMenu.Text = WorkspaceView.GetShortenedLanguageName(item.Text); return; } } diff --git a/src/BloomExe/Edit/EditingView.cs b/src/BloomExe/Edit/EditingView.cs index b315fe1a48e5..1b74e1fb5819 100644 --- a/src/BloomExe/Edit/EditingView.cs +++ b/src/BloomExe/Edit/EditingView.cs @@ -49,7 +49,7 @@ public partial class EditingView : UserControl, IBloomTabArea, IZoomManager private Color _disabledToolbarColor = Color.FromArgb(114, 74, 106); private bool _visible; private BloomWebSocketServer _webSocketServer; - private ZoomControl _zoomControl; + private ZoomModel _zoomModel; private PageListApi _pageListApi; private DateTime? _lastTopBarMenuClosedTime; @@ -1827,10 +1827,7 @@ out zoomFloat ) { zoomInt = (int)Math.Round(zoomFloat * 10F) * 10; - if ( - zoomInt < ZoomControl.kMinimumZoom - || zoomInt > ZoomControl.kMaximumZoom - ) + if (zoomInt < ZoomModel.kMinimumZoom || zoomInt > ZoomModel.kMaximumZoom) return 100; // bad antique value - normalize to real size. return zoomInt; } @@ -1850,10 +1847,10 @@ out zoomInt ) { // we can't go below 30 (30%), so those must be old floating point values that rounded to an integer. - if (zoomInt < ZoomControl.kMinimumZoom) + if (zoomInt < ZoomModel.kMinimumZoom) zoomInt = zoomInt * 100; - if (zoomInt > ZoomControl.kMaximumZoom) - return ZoomControl.kMaximumZoom; + if (zoomInt > ZoomModel.kMaximumZoom) + return ZoomModel.kMaximumZoom; return zoomInt; } else @@ -1878,20 +1875,20 @@ public void SetZoom(int zoom) public void AdjustPageZoom(int delta) { - var currentZoom = _zoomControl.Zoom; + var currentZoom = _zoomModel.Zoom; if ( - delta < 0 && currentZoom <= Bloom.Workspace.ZoomControl.kMinimumZoom - || delta > 0 && currentZoom >= Bloom.Workspace.ZoomControl.kMaximumZoom + delta < 0 && currentZoom <= ZoomModel.kMinimumZoom + || delta > 0 && currentZoom >= ZoomModel.kMaximumZoom ) { return; } - _zoomControl.Zoom = currentZoom + delta; + _zoomModel.Zoom = currentZoom + delta; } - internal void SetZoomControl(ZoomControl zoomCtl) + internal void SetZoomModel(ZoomModel zoomModel) { - _zoomControl = zoomCtl; + _zoomModel = zoomModel; } // intended for use only by the EditingModel diff --git a/src/BloomExe/Wizard/WinForms/WizardPage.cs b/src/BloomExe/Wizard/WinForms/WizardPage.cs index 96ce4ee6105e..175e3d59b8d0 100644 --- a/src/BloomExe/Wizard/WinForms/WizardPage.cs +++ b/src/BloomExe/Wizard/WinForms/WizardPage.cs @@ -161,8 +161,8 @@ private static void CreateUiLanguageMenuButton() private static void FinishUiLanguageMenuItemClick() { - ToolStripDropDownItem selectedItem = null; - foreach (ToolStripDropDownItem dropDownItem in s_toolStripDropDownButton.DropDownItems) + ToolStripItem selectedItem = null; + foreach (ToolStripItem dropDownItem in s_toolStripDropDownButton.DropDownItems) { if (dropDownItem.Selected) { diff --git a/src/BloomExe/Workspace/WorkspaceView.Designer.cs b/src/BloomExe/Workspace/WorkspaceView.Designer.cs index 5d69e5a6f14c..779f44a64d56 100644 --- a/src/BloomExe/Workspace/WorkspaceView.Designer.cs +++ b/src/BloomExe/Workspace/WorkspaceView.Designer.cs @@ -40,20 +40,17 @@ private void InitializeComponent() this.toolTip1 = new System.Windows.Forms.ToolTip(this.components); this._settingsLauncherHelper = new SIL.Windows.Forms.SettingProtection.SettingsProtectionHelper(this.components); this._containerPanel = new System.Windows.Forms.Panel(); - this._toolSpecificPanel = new System.Windows.Forms.Panel(); + this._toolSpecificTopBarPanel = new System.Windows.Forms.Panel(); this._L10NSharpExtender = new L10NSharp.Windows.Forms.L10NSharpExtender(this.components); this._tabStrip = new Messir.Windows.Forms.TabStrip(); this._editTab = new Messir.Windows.Forms.TabStripButton(); this._publishTab = new Messir.Windows.Forms.TabStripButton(); - this._toolStrip = new System.Windows.Forms.ToolStrip(); - this._uiLanguageMenu = new System.Windows.Forms.ToolStripDropDownButton(); - this._helpMenu = new System.Windows.Forms.ToolStripDropDownButton(); this._documentationMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this._bloomDocsMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this._trainingVideosMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.buildingReaderTemplatesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.usingReaderTemplatesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); + this._buildingReaderTemplatesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this._usingReaderTemplatesMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this._toolStripSeparator1 = new System.Windows.Forms.ToolStripSeparator(); this._askAQuestionMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this._requestAFeatureMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this._reportAProblemMenuItem = new System.Windows.Forms.ToolStripMenuItem(); @@ -64,13 +61,12 @@ private void InitializeComponent() this._divider2 = new System.Windows.Forms.ToolStripSeparator(); this._webSiteMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this._aboutBloomMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this._panelHoldingToolStrip = new Bloom.Workspace.NestedDockedChildPanel(); + this._panelHoldingTopRightReactControl = new Bloom.Workspace.NestedDockedChildPanel(); this._applicationUpdateCheckTimer = new System.Windows.Forms.Timer(this.components); this._reactCollectionTab = new Messir.Windows.Forms.TabStripButton(); ((System.ComponentModel.ISupportInitialize)(this._L10NSharpExtender)).BeginInit(); this._tabStrip.SuspendLayout(); - this._toolStrip.SuspendLayout(); - this._panelHoldingToolStrip.SuspendLayout(); + this._panelHoldingTopRightReactControl.SuspendLayout(); this.SuspendLayout(); // // _containerPanel @@ -83,15 +79,15 @@ private void InitializeComponent() this._containerPanel.Size = new System.Drawing.Size(1098, 463); this._containerPanel.TabIndex = 16; // - // _toolSpecificPanel + // _toolSpecificTopBarPanel // - this._toolSpecificPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) + this._toolSpecificTopBarPanel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); - this._toolSpecificPanel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(29)))), ((int)(((byte)(148)))), ((int)(((byte)(164))))); - this._toolSpecificPanel.Location = new System.Drawing.Point(357, 2); - this._toolSpecificPanel.Name = "_toolSpecificPanel"; - this._toolSpecificPanel.Size = new System.Drawing.Size(652, 60); - this._toolSpecificPanel.TabIndex = 17; + this._toolSpecificTopBarPanel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(29)))), ((int)(((byte)(148)))), ((int)(((byte)(164))))); + this._toolSpecificTopBarPanel.Location = new System.Drawing.Point(357, 2); + this._toolSpecificTopBarPanel.Name = "_toolSpecificTopBarPanel"; + this._toolSpecificTopBarPanel.Size = new System.Drawing.Size(652, 60); + this._toolSpecificTopBarPanel.TabIndex = 17; // // _L10NSharpExtender // @@ -167,69 +163,6 @@ private void InitializeComponent() this._publishTab.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; this._publishTab.TextChanged += new System.EventHandler(this.HandleTabTextChanged); // - // _toolStrip - // - this._toolStrip.BackColor = System.Drawing.Color.Transparent; - this._toolStrip.Dock = System.Windows.Forms.DockStyle.Right; - this._toolStrip.GripMargin = new System.Windows.Forms.Padding(0); - this._toolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; - this._toolStrip.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this._uiLanguageMenu, - this._helpMenu}); - this._L10NSharpExtender.SetLocalizableToolTip(this._toolStrip, null); - this._L10NSharpExtender.SetLocalizationComment(this._toolStrip, null); - this._L10NSharpExtender.SetLocalizationPriority(this._toolStrip, L10NSharp.LocalizationPriority.NotLocalizable); - this._L10NSharpExtender.SetLocalizingId(this._toolStrip, "WorkspaceView._toolStrip"); - this._toolStrip.Location = new System.Drawing.Point(30, 0); - this._toolStrip.Name = "_toolStrip"; - this._toolStrip.RightToLeft = System.Windows.Forms.RightToLeft.No; - this._toolStrip.Size = new System.Drawing.Size(59, 60); - this._toolStrip.TabIndex = 28; - this._toolStrip.Text = "_toolStrip"; - // - // _uiLanguageMenu - // - this._uiLanguageMenu.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Text; - this._uiLanguageMenu.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(49)))), ((int)(((byte)(32)))), ((int)(((byte)(46))))); - this._uiLanguageMenu.ImageTransparentColor = System.Drawing.Color.Magenta; - this._L10NSharpExtender.SetLocalizableToolTip(this._uiLanguageMenu, null); - this._L10NSharpExtender.SetLocalizationComment(this._uiLanguageMenu, null); - this._L10NSharpExtender.SetLocalizationPriority(this._uiLanguageMenu, L10NSharp.LocalizationPriority.NotLocalizable); - this._L10NSharpExtender.SetLocalizingId(this._uiLanguageMenu, ".toolStripDropDownButton1"); - this._uiLanguageMenu.Name = "_uiLanguageMenu"; - this._uiLanguageMenu.Size = new System.Drawing.Size(56, 19); - this._uiLanguageMenu.Text = "English"; - // - // _helpMenu - // - this._helpMenu.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this._documentationMenuItem, - this._bloomDocsMenuItem, - this._trainingVideosMenuItem, - this.buildingReaderTemplatesMenuItem, - this.usingReaderTemplatesMenuItem, - this.toolStripSeparator1, - this._askAQuestionMenuItem, - this._requestAFeatureMenuItem, - this._reportAProblemMenuItem, - this._divider1, - this._releaseNotesMenuItem, - this._checkForNewVersionMenuItem, - this._registrationMenuItem, - this._divider2, - this._webSiteMenuItem, - this._aboutBloomMenuItem}); - this._helpMenu.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(49)))), ((int)(((byte)(32)))), ((int)(((byte)(46))))); - this._helpMenu.Image = global::Bloom.Properties.Resources.help16x16Darker; - this._helpMenu.ImageScaling = System.Windows.Forms.ToolStripItemImageScaling.None; - this._helpMenu.ImageTransparentColor = System.Drawing.Color.Magenta; - this._L10NSharpExtender.SetLocalizableToolTip(this._helpMenu, "Get Help"); - this._L10NSharpExtender.SetLocalizationComment(this._helpMenu, null); - this._L10NSharpExtender.SetLocalizingId(this._helpMenu, "HelpMenu.Help Menu"); - this._helpMenu.Name = "_helpMenu"; - this._helpMenu.Size = new System.Drawing.Size(56, 20); - this._helpMenu.TextImageRelation = System.Windows.Forms.TextImageRelation.ImageAboveText; - // // _documentationMenuItem // this._documentationMenuItem.Image = global::Bloom.Properties.Resources.help24x24; @@ -263,34 +196,34 @@ private void InitializeComponent() this._trainingVideosMenuItem.Text = "Training Videos"; this._trainingVideosMenuItem.Click += new System.EventHandler(this._trainingVideosMenuItem_Click); // - // buildingReaderTemplatesMenuItem + // _buildingReaderTemplatesMenuItem // - this.buildingReaderTemplatesMenuItem.Image = global::Bloom.Properties.Resources.pdf16x16; - this._L10NSharpExtender.SetLocalizableToolTip(this.buildingReaderTemplatesMenuItem, null); - this._L10NSharpExtender.SetLocalizationComment(this.buildingReaderTemplatesMenuItem, null); - this._L10NSharpExtender.SetLocalizationPriority(this.buildingReaderTemplatesMenuItem, L10NSharp.LocalizationPriority.Low); - this._L10NSharpExtender.SetLocalizingId(this.buildingReaderTemplatesMenuItem, "HelpMenu.BuildingReaderTemplatesMenuItem"); - this.buildingReaderTemplatesMenuItem.Name = "buildingReaderTemplatesMenuItem"; - this.buildingReaderTemplatesMenuItem.Size = new System.Drawing.Size(213, 22); - this.buildingReaderTemplatesMenuItem.Text = "Building Reader Templates"; - this.buildingReaderTemplatesMenuItem.Click += new System.EventHandler(this.buildingReaderTemplatesMenuItem_Click); + this._buildingReaderTemplatesMenuItem.Image = global::Bloom.Properties.Resources.pdf16x16; + this._L10NSharpExtender.SetLocalizableToolTip(this._buildingReaderTemplatesMenuItem, null); + this._L10NSharpExtender.SetLocalizationComment(this._buildingReaderTemplatesMenuItem, null); + this._L10NSharpExtender.SetLocalizationPriority(this._buildingReaderTemplatesMenuItem, L10NSharp.LocalizationPriority.Low); + this._L10NSharpExtender.SetLocalizingId(this._buildingReaderTemplatesMenuItem, "HelpMenu.BuildingReaderTemplatesMenuItem"); + this._buildingReaderTemplatesMenuItem.Name = "_buildingReaderTemplatesMenuItem"; + this._buildingReaderTemplatesMenuItem.Size = new System.Drawing.Size(213, 22); + this._buildingReaderTemplatesMenuItem.Text = "Building Reader Templates"; + this._buildingReaderTemplatesMenuItem.Click += new System.EventHandler(this.buildingReaderTemplatesMenuItem_Click); // - // usingReaderTemplatesMenuItem + // _usingReaderTemplatesMenuItem // - this.usingReaderTemplatesMenuItem.Image = global::Bloom.Properties.Resources.pdf16x16; - this._L10NSharpExtender.SetLocalizableToolTip(this.usingReaderTemplatesMenuItem, null); - this._L10NSharpExtender.SetLocalizationComment(this.usingReaderTemplatesMenuItem, null); - this._L10NSharpExtender.SetLocalizationPriority(this.usingReaderTemplatesMenuItem, L10NSharp.LocalizationPriority.Low); - this._L10NSharpExtender.SetLocalizingId(this.usingReaderTemplatesMenuItem, "HelpMenu.UsingReaderTemplatesMenuItem"); - this.usingReaderTemplatesMenuItem.Name = "usingReaderTemplatesMenuItem"; - this.usingReaderTemplatesMenuItem.Size = new System.Drawing.Size(213, 22); - this.usingReaderTemplatesMenuItem.Text = "Using Reader Templates "; - this.usingReaderTemplatesMenuItem.Click += new System.EventHandler(this.usingReaderTemplatesMenuItem_Click); + this._usingReaderTemplatesMenuItem.Image = global::Bloom.Properties.Resources.pdf16x16; + this._L10NSharpExtender.SetLocalizableToolTip(this._usingReaderTemplatesMenuItem, null); + this._L10NSharpExtender.SetLocalizationComment(this._usingReaderTemplatesMenuItem, null); + this._L10NSharpExtender.SetLocalizationPriority(this._usingReaderTemplatesMenuItem, L10NSharp.LocalizationPriority.Low); + this._L10NSharpExtender.SetLocalizingId(this._usingReaderTemplatesMenuItem, "HelpMenu.UsingReaderTemplatesMenuItem"); + this._usingReaderTemplatesMenuItem.Name = "_usingReaderTemplatesMenuItem"; + this._usingReaderTemplatesMenuItem.Size = new System.Drawing.Size(213, 22); + this._usingReaderTemplatesMenuItem.Text = "Using Reader Templates "; + this._usingReaderTemplatesMenuItem.Click += new System.EventHandler(this.usingReaderTemplatesMenuItem_Click); // - // toolStripSeparator1 + // _toolStripSeparator1 // - this.toolStripSeparator1.Name = "toolStripSeparator1"; - this.toolStripSeparator1.Size = new System.Drawing.Size(210, 6); + this._toolStripSeparator1.Name = "toolStripSeparator1"; + this._toolStripSeparator1.Size = new System.Drawing.Size(210, 6); // // _askAQuestionMenuItem // @@ -386,19 +319,18 @@ private void InitializeComponent() this._aboutBloomMenuItem.Text = "About Bloom..."; this._aboutBloomMenuItem.Click += new System.EventHandler(this.OnAboutBoxClick); // - // _panelHoldingToolStrip + // _panelHoldingTopRightReactControl // - this._panelHoldingToolStrip.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this._panelHoldingToolStrip.BackColor = System.Drawing.Color.Transparent; - this._panelHoldingToolStrip.Controls.Add(this._toolStrip); - this._L10NSharpExtender.SetLocalizableToolTip(this._panelHoldingToolStrip, null); - this._L10NSharpExtender.SetLocalizationComment(this._panelHoldingToolStrip, null); - this._L10NSharpExtender.SetLocalizationPriority(this._panelHoldingToolStrip, L10NSharp.LocalizationPriority.NotLocalizable); - this._L10NSharpExtender.SetLocalizingId(this._panelHoldingToolStrip, "HelpMenu.WorkspaceView._panelHoldingToolStrip"); - this._panelHoldingToolStrip.Location = new System.Drawing.Point(1006, 3); - this._panelHoldingToolStrip.Name = "_panelHoldingToolStrip"; - this._panelHoldingToolStrip.Size = new System.Drawing.Size(89, 59); - this._panelHoldingToolStrip.TabIndex = 29; + this._panelHoldingTopRightReactControl.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); + this._panelHoldingTopRightReactControl.BackColor = System.Drawing.Color.Transparent; + this._L10NSharpExtender.SetLocalizableToolTip(this._panelHoldingTopRightReactControl, null); + this._L10NSharpExtender.SetLocalizationComment(this._panelHoldingTopRightReactControl, null); + this._L10NSharpExtender.SetLocalizationPriority(this._panelHoldingTopRightReactControl, L10NSharp.LocalizationPriority.NotLocalizable); + this._L10NSharpExtender.SetLocalizingId(this._panelHoldingTopRightReactControl, "HelpMenu.WorkspaceView._panelHoldingTopRightReactControl"); + this._panelHoldingTopRightReactControl.Location = new System.Drawing.Point(1006, 3); + this._panelHoldingTopRightReactControl.Name = "_panelHoldingTopRightReactControl"; + this._panelHoldingTopRightReactControl.Size = new System.Drawing.Size(89, 59); + this._panelHoldingTopRightReactControl.TabIndex = 29; // // _applicationUpdateCheckTimer // @@ -431,8 +363,8 @@ private void InitializeComponent() // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Controls.Add(this._panelHoldingToolStrip); - this.Controls.Add(this._toolSpecificPanel); + this.Controls.Add(this._panelHoldingTopRightReactControl); + this.Controls.Add(this._toolSpecificTopBarPanel); this.Controls.Add(this._containerPanel); this.Controls.Add(this._tabStrip); this._L10NSharpExtender.SetLocalizableToolTip(this, null); @@ -441,14 +373,11 @@ private void InitializeComponent() this.Name = "WorkspaceView"; this.Size = new System.Drawing.Size(1098, 528); this.Load += new System.EventHandler(this.WorkspaceView_Load); - this.Resize += new System.EventHandler(this.WorkspaceView_Resize); ((System.ComponentModel.ISupportInitialize)(this._L10NSharpExtender)).EndInit(); this._tabStrip.ResumeLayout(false); this._tabStrip.PerformLayout(); - this._toolStrip.ResumeLayout(false); - this._toolStrip.PerformLayout(); - this._panelHoldingToolStrip.ResumeLayout(false); - this._panelHoldingToolStrip.PerformLayout(); + this._panelHoldingTopRightReactControl.ResumeLayout(false); + this._panelHoldingTopRightReactControl.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); @@ -459,21 +388,18 @@ private void InitializeComponent() private System.Windows.Forms.ToolTip toolTip1; private SIL.Windows.Forms.SettingProtection.SettingsProtectionHelper _settingsLauncherHelper; private System.Windows.Forms.Panel _containerPanel; - private System.Windows.Forms.Panel _toolSpecificPanel; + private System.Windows.Forms.Panel _toolSpecificTopBarPanel; private Messir.Windows.Forms.TabStripButton _editTab; private Messir.Windows.Forms.TabStripButton _publishTab; private Messir.Windows.Forms.TabStrip _tabStrip; private L10NSharp.Windows.Forms.L10NSharpExtender _L10NSharpExtender; private System.Windows.Forms.Timer _applicationUpdateCheckTimer; - private System.Windows.Forms.ToolStrip _toolStrip; - private System.Windows.Forms.ToolStripDropDownButton _uiLanguageMenu; - private System.Windows.Forms.ToolStripDropDownButton _helpMenu; private System.Windows.Forms.ToolStripMenuItem _documentationMenuItem; private System.Windows.Forms.ToolStripMenuItem _trainingVideosMenuItem; - private System.Windows.Forms.ToolStripSeparator toolStripSeparator1; + private System.Windows.Forms.ToolStripSeparator _toolStripSeparator1; private System.Windows.Forms.ToolStripMenuItem _releaseNotesMenuItem; - private System.Windows.Forms.ToolStripMenuItem buildingReaderTemplatesMenuItem; - private System.Windows.Forms.ToolStripMenuItem usingReaderTemplatesMenuItem; + private System.Windows.Forms.ToolStripMenuItem _buildingReaderTemplatesMenuItem; + private System.Windows.Forms.ToolStripMenuItem _usingReaderTemplatesMenuItem; private System.Windows.Forms.ToolStripMenuItem _reportAProblemMenuItem; private System.Windows.Forms.ToolStripMenuItem _requestAFeatureMenuItem; private System.Windows.Forms.ToolStripMenuItem _webSiteMenuItem; @@ -482,7 +408,7 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem _checkForNewVersionMenuItem; private System.Windows.Forms.ToolStripMenuItem _registrationMenuItem; private System.Windows.Forms.ToolStripMenuItem _aboutBloomMenuItem; - private NestedDockedChildPanel _panelHoldingToolStrip; + private NestedDockedChildPanel _panelHoldingTopRightReactControl; private System.Windows.Forms.ToolStripMenuItem _askAQuestionMenuItem; private Messir.Windows.Forms.TabStripButton _reactCollectionTab; private System.Windows.Forms.ToolStripMenuItem _bloomDocsMenuItem; diff --git a/src/BloomExe/Workspace/WorkspaceView.cs b/src/BloomExe/Workspace/WorkspaceView.cs index 526785ffe06d..6af1b19c67cb 100644 --- a/src/BloomExe/Workspace/WorkspaceView.cs +++ b/src/BloomExe/Workspace/WorkspaceView.cs @@ -46,19 +46,18 @@ public partial class WorkspaceView : UserControl private bool _viewInitialized; private int _originalToolStripPanelWidth; private int _originalToolSpecificPanelHorizPos; - private int _originalUiMenuWidth; private int _stage1SpaceSaved; private int _stage2SpaceSaved; - private string _originalHelpText; - private Image _originalHelpImage; - private string _originalUiLanguageSelection; private EditingView _editingView; private PublishView _publishView; private CollectionTabView _collectionTabView; private Control _previouslySelectedControl; public event EventHandler ReopenCurrentProject; public static float DPIOfThisAccount; - private ZoomControl _zoomControl; + private ZoomModel _zoomModel; + private ReactControl _topRightReactControl; + private readonly ContextMenuStrip _uiLanguageContextMenu = new ContextMenuStrip(); + private readonly ContextMenuStrip _helpContextMenu = new ContextMenuStrip(); public delegate WorkspaceView Factory(); @@ -152,8 +151,6 @@ TeamCollectionApi teamCollectionApi _checkForNewVersionMenuItem.Visible = Platform.IsWindows; - _toolStrip.Renderer = new NoBorderToolStripRenderer(); - //we have a number of buttons which don't make sense for the remote (therefore vulnerable) low-end user //_settingsLauncherHelper.CustomSettingsControl = _toolStrip; //NB: these aren't really settings, but we're using that feature to simplify this menu down to what makes sense for the easily-confused user @@ -162,9 +159,6 @@ TeamCollectionApi teamCollectionApi _settingsLauncherHelper.ManageComponent(_releaseNotesMenuItem); _settingsLauncherHelper.ManageComponent(_divider2); - _uiLanguageMenu.Visible = true; - _settingsLauncherHelper.ManageComponent(_uiLanguageMenu); - editBookCommand.Subscribe(OnEditBook); Application.Idle += new EventHandler(Application_Idle); @@ -203,7 +197,7 @@ TeamCollectionApi teamCollectionApi this._reactCollectionTab.Text = _collectionTabView.CollectionTabLabel; _tabStrip.SelectedTab = _reactCollectionTab; - SelectPage(_collectionTabView); + SelectTab(_collectionTabView); if (Platform.IsMono) { @@ -213,15 +207,9 @@ TeamCollectionApi teamCollectionApi _tabStrip.AutoSize = false; } - _toolStrip.SizeChanged += ToolStripOnSizeChanged; - - _uiLanguageMenu.DropDownOpening += (sender, args) => - { - SetupUiLanguageMenu(); - }; - SetupUiLanguageMenu(true); - SetupZoomControl(); - AdjustButtonTextsForLocale(); + SetupZoomModel(); + SetupTopRightReactControl(); + SendZoomInfo(); _viewInitialized = false; CommonApi.WorkspaceView = this; @@ -501,17 +489,165 @@ private void HandleBookStatusChange(BookStatusChangeEventArgs args) } } - private void SetupZoomControl() + private void SetupZoomModel() { - _zoomControl = new ZoomControl(); - _zoomWrapper = new ToolStripControlHost(_zoomControl); - // We're using a ToolStrip to display these three controls in the top right, and it does a nice job - // of stretching the width to match localization. But height and spacing we must control exactly, - // or it goes into an overflow mode that is very ugly. - _zoomWrapper.Margin = Padding.Empty; + _zoomModel = new ZoomModel(); + _zoomModel.ZoomChanged += OnZoomChanged; // Provide access for javascript to adjust this control via the EditingView and EditingModel. // See https://issues.bloomlibrary.org/youtrack/issue/BL-5584. - _editingView.SetZoomControl(_zoomControl); + _editingView.SetZoomModel(_zoomModel); + } + + public void SetZoom(int zoom) + { + if (_zoomModel == null) + return; + + _zoomModel.Zoom = zoom; + } + + private void OnZoomChanged(object sender, EventArgs e) + { + if (CurrentTabView is IZoomManager zoomManager) + zoomManager.SetZoom(_zoomModel.Zoom); + SendZoomInfo(); + } + + private void SetupTopRightReactControl() + { + _topRightReactControl = ReactControl.Create("workspaceTopRightControlsBundle"); + _topRightReactControl.HideVerticalOverflow = true; + _topRightReactControl.Dock = DockStyle.Fill; + _topRightReactControl.BackColor = _panelHoldingTopRightReactControl.BackColor; + _topRightReactControl.SetLocalizationChangedEvent(_localizationChangedEvent); + _panelHoldingTopRightReactControl.Controls.Add(_topRightReactControl); + // Nothing special about 125. It just gives room for the longest language name (Bahasa Indonesia). + // But this is going to go away very soon as the whole top bar becomes one control. + if (_panelHoldingTopRightReactControl.Width < 125) + { + _panelHoldingTopRightReactControl.Width = 125; + + _panelHoldingTopRightReactControl.Left = + this.Width - _panelHoldingTopRightReactControl.Width; // align this panel on the right. + _toolSpecificTopBarPanel.Width = + _panelHoldingTopRightReactControl.Left - _toolSpecificTopBarPanel.Left; + } + } + + public dynamic GetZoomInfo() + { + var zoomManager = CurrentTabView as IZoomManager; + var zoomEnabled = zoomManager != null; + var zoomValue = zoomEnabled ? zoomManager.Zoom : (_zoomModel?.Zoom ?? 100); + dynamic zoomInfo = new DynamicJson(); + zoomInfo.zoom = zoomValue; + zoomInfo.zoomEnabled = zoomEnabled; + zoomInfo.minZoom = ZoomModel.kMinimumZoom; + zoomInfo.maxZoom = ZoomModel.kMaximumZoom; + return zoomInfo; + } + + public string GetCurrentUiLanguageLabel() + { + var lang = Settings.Default.UserInterfaceLanguage; + if (String.IsNullOrEmpty(lang)) + lang = "en"; + var item = CreateLanguageItem(lang); + return GetShortenedLanguageName(item.MenuText); + } + + private static List GetLanguageItems(bool onlyActiveItem) + { + var items = new List(); + if (onlyActiveItem) + { + if (String.IsNullOrEmpty(Settings.Default.UserInterfaceLanguage)) + Settings.Default.UserInterfaceLanguage = "en"; // See BL-13545. + items.Add(CreateLanguageItem(Settings.Default.UserInterfaceLanguage)); + } + else + { + foreach (var lang in LocalizationManager.GetAvailableLocalizedLanguages()) + { + var approved = FractionApproved(lang); + if (Settings.Default.ShowUnapprovedLocalizations) + approved = FractionTranslated(lang); + var alpha = ApplicationUpdateSupport.IsDevOrAlpha; + if ((alpha && approved < 0.01F) || (!alpha && approved < 0.25F)) + continue; + items.Add(CreateLanguageItem(lang)); + } + } + + items.Sort(compareLangItems); + return items; + } + + public void ShowUiLanguageMenu() + { + SetupUiLanguageMenu(); + ShowContextMenu(_uiLanguageContextMenu); + } + + public void ShowHelpMenu() + { + BuildHelpContextMenu(); + ShowContextMenu(_helpContextMenu); + } + + private void BuildHelpContextMenu() + { + _helpContextMenu.Items.Clear(); + _helpContextMenu.Items.AddRange( + new ToolStripItem[] + { + _documentationMenuItem, + _bloomDocsMenuItem, + _trainingVideosMenuItem, + _buildingReaderTemplatesMenuItem, + _usingReaderTemplatesMenuItem, + _toolStripSeparator1, + _askAQuestionMenuItem, + _requestAFeatureMenuItem, + _reportAProblemMenuItem, + _divider1, + _releaseNotesMenuItem, + _checkForNewVersionMenuItem, + _registrationMenuItem, + _divider2, + _webSiteMenuItem, + _aboutBloomMenuItem, + } + ); + } + + private void ShowContextMenu(ContextMenuStrip menu) + { + // Align the menu's right edge with the window's right edge. + // Ensures it stays on the same monitor. + // But also, it provides more consistency than having it shift left/right + // depending on where the mouse is. + var host = FindForm(); + var windowRight = host?.Bounds.Right ?? MousePosition.X; + var menuWidth = menu.Width > 0 ? menu.Width : menu.GetPreferredSize(Size.Empty).Width; + var x = windowRight - menuWidth; + var y = MousePosition.Y + 8; + + var timer = new System.Windows.Forms.Timer { Interval = 10 }; + timer.Tick += (s, a) => + { + menu.Left = x; + menu.Top = y; + menu.Show(x, y); + timer.Stop(); + timer.Dispose(); + }; + timer.Start(); + } + + private void SendZoomInfo() + { + _webSocketServer?.SendBundle("workspaceTopRightControls", "zoom", GetZoomInfo()); } private int TabButtonSectionWidth @@ -526,11 +662,11 @@ private int TabButtonSectionWidth void AdjustToolPanelLocation(bool allowNarrowing) { var widthOfTabButtons = TabButtonSectionWidth; - var location = _toolSpecificPanel.Location; + var location = _toolSpecificTopBarPanel.Location; if (widthOfTabButtons > location.X || allowNarrowing) { location.X = widthOfTabButtons; - _toolSpecificPanel.Location = location; + _toolSpecificTopBarPanel.Location = location; } } @@ -617,33 +753,95 @@ private void _applicationUpdateCheckTimer_Tick(object sender, EventArgs e) ToolStripMenuItem _showAllTranslationsItem; - private void SetupUiLanguageMenu(bool onlyActiveItem = false) - { - SetupUiLanguageMenuCommon( - _uiLanguageMenu, - FinishUiLanguageMenuItemClick, - onlyActiveItem + private void SetupUiLanguageMenu() + { + var items = GetLanguageItems(onlyActiveItem: false); + var tooltipFormat = GetUiLanguageTooltipFormat(); + var current = GetAndNormalizeCurrentUiLanguage(); + _uiLanguageContextMenu.Items.Clear(); + AddUiLanguageMenuItems( + _uiLanguageContextMenu.Items, + items, + current, + tooltipFormat, + checkCurrentItem: true, + (langItem) => SetUiLanguage(langItem.LangTag), + onCurrentItemAdded: null ); - // REVIEW: should this be part of SetupUiLanguageMenuCommon()? should it be added only for alpha and beta? - _uiLanguageMenu.DropDownItems.Add("-"); - _showAllTranslationsItem = new ToolStripMenuItem(); - _showAllTranslationsItem.Text = GetShowUnapprovedTranslationsMenuText(); - _showAllTranslationsItem.Checked = Settings.Default.ShowUnapprovedLocalizations; + _uiLanguageContextMenu.Items.Add(new ToolStripSeparator()); + _showAllTranslationsItem = new ToolStripMenuItem( + GetShowUnapprovedTranslationsMenuText() + ) + { + Checked = Settings.Default.ShowUnapprovedLocalizations, + }; _showAllTranslationsItem.Click += (sender, args) => ToggleShowingOnlyApprovedTranslations(); - _uiLanguageMenu.DropDownItems.Add(_showAllTranslationsItem); + _uiLanguageContextMenu.Items.Add(_showAllTranslationsItem); + + AddHelpTranslateMenuItem(_uiLanguageContextMenu.Items); + } + + private static string GetUiLanguageTooltipFormat() + { + return LocalizationManager.GetString( + "CollectionTab.UILanguageMenu.ItemTooltip", + "{0}% translated", + "Shown when hovering over an item in the UI Language menu. The {0} marker is filled in by a number between 1 and 100." + ); + } + + private static string GetAndNormalizeCurrentUiLanguage() + { + var current = Settings.Default.UserInterfaceLanguage; + if (String.IsNullOrEmpty(current)) + current = "en"; + Settings.Default.UserInterfaceLanguage = current; + return current; + } + + private static void AddUiLanguageMenuItems( + ToolStripItemCollection target, + IEnumerable items, + string current, + string tooltipFormat, + bool checkCurrentItem, + Action onSelect, + Action onCurrentItemAdded + ) + { + foreach (var langItem in items) + { + var translationFraction = Settings.Default.ShowUnapprovedLocalizations + ? langItem.FractionTranslated + : langItem.FractionApproved; + var toolTip = String.Format(tooltipFormat, (int)(translationFraction * 100.0F)); + var item = new ToolStripMenuItem(langItem.MenuText) + { + Checked = checkCurrentItem && langItem.LangTag == current, + Tag = langItem, + ToolTipText = toolTip, + }; + item.Click += (sender, args) => onSelect(langItem); + target.Add(item); + if (langItem.LangTag == current) + onCurrentItemAdded?.Invoke(langItem); + } + } - _uiLanguageMenu.DropDown.Closing += DropDown_Closing; - _helpMenu.DropDown.Closing += DropDown_Closing; - _uiLanguageMenu.DropDown.Opening += DropDown_Opening; - _helpMenu.DropDown.Opening += DropDown_Opening; - // one side-effect of the above is if the _uiLanguageMenu dropdown is open, a click on the _helpMenu won't close it - // (and vice versa) - _helpMenu.Click += (sender, args) => - _uiLanguageMenu.DropDown.Close(ToolStripDropDownCloseReason.ItemClicked); - _uiLanguageMenu.Click += (sender, e) => - _helpMenu.DropDown.Close(ToolStripDropDownCloseReason.ItemClicked); + private static void AddHelpTranslateMenuItem(ToolStripItemCollection target) + { + target.Add(new ToolStripSeparator()); + var message = LocalizationManager.GetString( + "CollectionTab.UILanguageMenu.HelpTranslate", + "Help us translate Bloom (web)", + "The final item in the UI Language menu. When clicked, it opens Bloom's page in the Crowdin web-based translation system." + ); + var helpItem = target.Add(message); + helpItem.Image = Resources.weblink; + helpItem.Click += (sender, args) => + ProcessExtra.SafeStartInFront(UrlLookup.LookupUrl(UrlType.LocalizingSystem, null)); } private void ToggleShowingOnlyApprovedTranslations() @@ -661,61 +859,6 @@ private void ToggleShowingOnlyApprovedTranslations() Program.RestartBloom(false); } - private bool _ignoreNextAppFocusChange; - - /// - /// Prevent undesirable closing of dropdown menus. This is worth losing some desired - /// closings, especially for Linux/Gnome in which the menus refuse to stay open at all - /// without this fix. - /// - /// - /// See https://silbloom.myjetbrains.com/youtrack/issue/BL-5471. - /// See https://silbloom.myjetbrains.com/youtrack/issue/BL-6107. - /// The exact behavior seems rather system dependent. - /// - private void DropDown_Closing(object sender, ToolStripDropDownClosingEventArgs e) - { - // ReSharper disable once SwitchStatementMissingSomeCases - switch (e.CloseReason) - { - case ToolStripDropDownCloseReason.AppFocusChange: - // this is usually just hovering over the help menu on Windows. - // there is always one spurious focus change on Linux/Gnome. - e.Cancel = _ignoreNextAppFocusChange; - break; - case ToolStripDropDownCloseReason.AppClicked: - // "reason" is AppClicked, but is it legit? - // Every other time we get AppClicked even if we are just hovering over the help menu. - case ToolStripDropDownCloseReason.Keyboard: - // If "reason" is Keyboard, that seems to be generated just by moving the mouse over the - // adjacent (visible) button on Linux. - var mousePos = _helpMenu.Owner.PointToClient(MousePosition); - var bounds = - (sender == _helpMenu.DropDown) ? _uiLanguageMenu.Bounds : _helpMenu.Bounds; - if (bounds.Contains(mousePos)) - { - // probably a false positive - e.Cancel = - e.CloseReason == ToolStripDropDownCloseReason.AppClicked - || Platform.IsLinux; - } - break; - default: // includes ItemClicked, CloseCalled - break; - } - _ignoreNextAppFocusChange = Platform.IsWindows; // preserve the fix for BL-5476. - Debug.WriteLine( - "DEBUG WorkspaceView.DropDown_Closing: reason={0}, cancel={1}", - e.CloseReason.ToString(), - e.Cancel - ); - } - - void DropDown_Opening(object sender, CancelEventArgs e) - { - _ignoreNextAppFocusChange = true; - } - /// /// This is also called by CollectionChoosing.OpenCreateCloneControl /// @@ -725,64 +868,26 @@ public static void SetupUiLanguageMenuCommon( bool onlyActiveItem = false ) { - var items = new List(); - if (onlyActiveItem) - { - if (String.IsNullOrEmpty(Settings.Default.UserInterfaceLanguage)) - Settings.Default.UserInterfaceLanguage = "en"; // See BL-13545. - items.Add(CreateLanguageItem(Settings.Default.UserInterfaceLanguage)); - } - else - { - foreach (var lang in LocalizationManager.GetAvailableLocalizedLanguages()) - { - // Require that at least 1% of the strings have been translated and approved for alphas, - // or 25% translated and approved for betas and release. - var approved = FractionApproved(lang); - if (Settings.Default.ShowUnapprovedLocalizations) - approved = FractionTranslated(lang); - var alpha = ApplicationUpdateSupport.IsDevOrAlpha; - if ((alpha && approved < 0.01F) || (!alpha && approved < 0.25F)) - continue; - items.Add(CreateLanguageItem(lang)); - } - } - - items.Sort(compareLangItems); - - var tooltipFormat = LocalizationManager.GetString( - "CollectionTab.UILanguageMenu.ItemTooltip", - "{0}% translated", - "Shown when hovering over an item in the UI Language menu. The {0} marker is filled in by a number between 1 and 100." - ); + var items = GetLanguageItems(onlyActiveItem); + var tooltipFormat = GetUiLanguageTooltipFormat(); + var current = GetAndNormalizeCurrentUiLanguage(); uiMenuControl.DropDownItems.Clear(); - foreach (var langItem in items) - { - var item = uiMenuControl.DropDownItems.Add(langItem.MenuText); - item.Tag = langItem; - var fraction = langItem.FractionApproved; - if (Settings.Default.ShowUnapprovedLocalizations) - fraction = langItem.FractionTranslated; - item.ToolTipText = String.Format(tooltipFormat, (int)(fraction * 100.0F)); - item.Click += (sender, args) => + AddUiLanguageMenuItems( + uiMenuControl.DropDownItems, + items, + current, + tooltipFormat, + checkCurrentItem: false, + (langItem) => UiLanguageMenuItemClickHandler( uiMenuControl, - sender as ToolStripItem, + langItem.LangTag, finishClickAction - ); - if (langItem.LangTag == Settings.Default.UserInterfaceLanguage) - UpdateMenuTextToShorterNameOfSelection(uiMenuControl, langItem.MenuText); - } - uiMenuControl.DropDownItems.Add("-"); // adds ToolStripSeparator - var message = LocalizationManager.GetString( - "CollectionTab.UILanguageMenu.HelpTranslate", - "Help us translate Bloom (web)", - "The final item in the UI Language menu. When clicked, it opens Bloom's page in the Crowdin web-based translation system." + ), + onCurrentItemAdded: (langItem) => + uiMenuControl.Text = GetShortenedLanguageName(langItem.MenuText) ); - var helpItem = uiMenuControl.DropDownItems.Add(message); - helpItem.Image = Resources.weblink; - helpItem.Click += (sender, args) => - ProcessExtra.SafeStartInFront(UrlLookup.LookupUrl(UrlType.LocalizingSystem, null)); + AddHelpTranslateMenuItem(uiMenuControl.DropDownItems); } private static int compareLangItems(LanguageItem a, LanguageItem b) @@ -800,21 +905,13 @@ private static int compareLangItems(LanguageItem a, LanguageItem b) ); } - private static void UiLanguageMenuItemClickHandler( - ToolStripDropDownButton toolStripButton, - ToolStripItem item, - Action finishClickAction - ) + private static void ApplyUiLanguageChange(string langTag) { - var tag = (LanguageItem)item.Tag; - - LocalizationManagerWinforms.SetUILanguage(tag.LangTag, true); + LocalizationManagerWinforms.SetUILanguage(langTag, true); // TODO-WV2: Can we set the browser language in WV2? Do we need to? - Settings.Default.UserInterfaceLanguage = tag.LangTag; + Settings.Default.UserInterfaceLanguage = langTag; Settings.Default.UserInterfaceLanguageSetExplicitly = true; Settings.Default.Save(); - item.Select(); - UpdateMenuTextToShorterNameOfSelection(toolStripButton, item.Text); // Currently needed for the language chooser to update its localization // BloomWebSocketServer.Instance is set only while loading a collection. @@ -827,22 +924,38 @@ Action finishClickAction server.Init( (BloomServer.portForHttp + 1).ToString(CultureInfo.InvariantCulture) ); - server.SendString("app", "uiLanguageChanged", tag.LangTag); + server.SendString("app", "uiLanguageChanged", langTag); } } else { - BloomWebSocketServer.Instance.SendString("app", "uiLanguageChanged", tag.LangTag); + BloomWebSocketServer.Instance.SendString("app", "uiLanguageChanged", langTag); } + } + private static void UiLanguageMenuItemClickHandler( + ToolStripDropDownButton toolStripButton, + string langTag, + Action finishClickAction + ) + { + ApplyUiLanguageChange(langTag); + if (toolStripButton != null) + { + var menuText = CreateLanguageItem(langTag).MenuText; + toolStripButton.Text = GetShortenedLanguageName(menuText); + } finishClickAction?.Invoke(); } + public void SetUiLanguage(string langTag) + { + ApplyUiLanguageChange(langTag); + FinishUiLanguageMenuItemClick(); + } + private void FinishUiLanguageMenuItemClick() { - // these lines deal with having a smaller workspace window and minimizing the button texts for smaller windows - SaveOriginalButtonTexts(); - AdjustButtonTextsForLocale(); _showAllTranslationsItem.Text = GetShowUnapprovedTranslationsMenuText(); _localizationChangedEvent.Raise(null); } @@ -904,24 +1017,21 @@ public static float FractionTranslated(string code) return (float)translatedCount / (float)totalCount; } - public static void UpdateMenuTextToShorterNameOfSelection( - ToolStripDropDownButton toolStripButton, - string itemText - ) + public static string GetShortenedLanguageName(string itemText) { var idxChinese = itemText.IndexOf(" (Chinese"); if (idxChinese > 0) { - toolStripButton.Text = itemText.Substring(0, idxChinese); + return itemText.Substring(0, idxChinese); } else { var idxCountry = itemText.IndexOf(" ("); if (idxCountry > 0) - toolStripButton.Text = itemText.Substring(0, idxCountry); + return itemText.Substring(0, idxCountry); else { - toolStripButton.Text = itemText; + return itemText; } } } @@ -1079,9 +1189,9 @@ public void CheckForInvalidBranding() ); } - private void SelectPage(Control view) + private void SelectTab(Control view) { - // Already on the desired page: nothing to do. And possible problems if we do do something. + // Already on the desired tab: nothing to do. And possible problems if we do do something. // See https://issues.bloomlibrary.org/youtrack/issue/BL-8382. if (view == _previouslySelectedControl) return; @@ -1107,20 +1217,19 @@ private void SelectPage(Control view) view.Dock = DockStyle.Fill; _containerPanel.Controls.Add(view); - _toolSpecificPanel.Controls.Clear(); + _toolSpecificTopBarPanel.Controls.Clear(); - _panelHoldingToolStrip.BackColor = CurrentTabView.TopBarControl.BackColor = + _panelHoldingTopRightReactControl.BackColor = CurrentTabView.TopBarControl.BackColor = _tabStrip.BackColor; - - if (Platform.IsMono) + if (_topRightReactControl != null) // might not be set up yet { - BackgroundColorsForLinux(CurrentTabView); + _topRightReactControl.BackColor = _tabStrip.BackColor; } if (CurrentTabView != null) //can remove when we get rid of info view { CurrentTabView.PlaceTopBarControl(); - _toolSpecificPanel.Controls.Add(CurrentTabView.TopBarControl); + _toolSpecificTopBarPanel.Controls.Add(CurrentTabView.TopBarControl); CurrentTabView.TopBarControl.Dock = DockStyle.Fill; } @@ -1141,41 +1250,15 @@ private void SelectPage(Control view) var zoomManager = CurrentTabView as IZoomManager; if (zoomManager != null) { - if (!_toolStrip.Items.Contains(_zoomWrapper)) - _toolStrip.Items.Add(_zoomWrapper); - _zoomControl.Zoom = zoomManager.Zoom; - _zoomControl.ZoomChanged += (sender, args) => - zoomManager.SetZoom(_zoomControl.Zoom); - } - else - { - if (_toolStrip.Items.Contains(_zoomWrapper)) - _toolStrip.Items.Remove(_zoomWrapper); + _zoomModel.Zoom = zoomManager.Zoom; } + SendZoomInfo(); // TODO-WV2: Can we clear the cache in WV2? Do we need to? }, } ); } - private void BackgroundColorsForLinux(IBloomTabArea currentTabView) - { - if (currentTabView.ToolStripBackground == null) - { - var bmp = new Bitmap(_toolStrip.Width, _toolStrip.Height); - using (var g = Graphics.FromImage(bmp)) - { - using (var b = new SolidBrush(_panelHoldingToolStrip.BackColor)) - { - g.FillRectangle(b, 0, 0, bmp.Width, bmp.Height); - } - } - currentTabView.ToolStripBackground = bmp; - } - - _toolStrip.BackgroundImage = currentTabView.ToolStripBackground; - } - protected IBloomTabArea CurrentTabView { get; set; } private void _tabStrip_SelectedTabChanged(object sender, SelectedTabChangedEventArgs e) @@ -1196,10 +1279,14 @@ private void _tabStrip_SelectedTabChanged(object sender, SelectedTabChangedEvent } TabStripButton btn = (TabStripButton)e.SelectedTab; _tabStrip.BackColor = btn.BarColor; - _toolSpecificPanel.BackColor = _panelHoldingToolStrip.BackColor = _tabStrip.BackColor; + _toolSpecificTopBarPanel.BackColor = _panelHoldingTopRightReactControl.BackColor = + _tabStrip.BackColor; + if (_topRightReactControl != null) // might not be set up yet + { + _topRightReactControl.BackColor = _tabStrip.BackColor; + } //Logger.WriteEvent("Selecting Tab Page: " + e.SelectedTab.Name); - SelectPage((Control)e.SelectedTab.Tag); - AdjustTabStripDisplayForScreenSize(); + SelectTab((Control)e.SelectedTab.Tag); if (_tabSelection.ActiveTab == WorkspaceTab.collection && _collectionTabView != null) { if (Publish.BloomLibrary.BloomLibraryPublishModel.BookUploaded) @@ -1284,14 +1371,6 @@ private void _showLogMenuItem_Click(object sender, EventArgs e) catch (Exception) { } } - private void WorkspaceView_Resize(object sender, EventArgs e) - { - if (this.ParentForm != null && this.ParentForm.WindowState != FormWindowState.Minimized) - { - AdjustTabStripDisplayForScreenSize(); - } - } - private void WorkspaceView_Load(object sender, EventArgs e) { CheckDPISettings(); @@ -1587,250 +1666,6 @@ private void _trainingVideosMenuItem_Click(object sender, EventArgs e) dlg.ShowDialog(); } } - - #region Responsive Toolbar - - enum Shrinkage - { - FullSize, - Stage1, - Stage2, - Stage3, - } - - private Shrinkage _currentShrinkage = Shrinkage.FullSize; - private ToolStripControlHost _zoomWrapper; - - private const int MinToolStripMargin = 3; - - // The width of the toolstrip panel in stage 1 is typically its original width, which leaves a bit of margin - // left of the toolstrip. If a long language name requires more width than typical, make it at least wide - // enough to hold the language name. In the latter case, stage 2 won't do anything, and we will move right - // on to stage 3 if stage 1 isn't enough. - private int Stage_1WidthOfToolStringPanel => - Math.Max(_originalToolStripPanelWidth, _toolStrip.Width + MinToolStripMargin); - - // The width at which we switch to stage 1: the actual space needed for the controls in the top panel, - // when each is in its widest form and the preferred extra space is between the tab controls and the TopBarControl. - // Since this is meant to be BEFORE we push the _toolSpecificPanel up against the tabs, we use its location - // rather than the with of the tabs. - private int STAGE_1 => - _originalToolSpecificPanelHorizPos - + (CurrentTabView?.WidthToReserveForTopBarControl ?? 0) - + Stage_1WidthOfToolStringPanel; - - private int STAGE_2 - { - get { return STAGE_1 - _stage1SpaceSaved; } - } - private int STAGE_3 - { - get { return STAGE_2 - _stage2SpaceSaved; } - } - - // The tabstrip typically changes size when a different language is selected. - - private void ToolStripOnSizeChanged(object o, EventArgs eventArgs) - { - AdjustTabStripDisplayForScreenSize(); - } - - private void AdjustTabStripDisplayForScreenSize() - { - if (!_viewInitialized) - return; - - if (_originalToolStripPanelWidth == 0) - { - SaveOriginalWidthValues(); - SaveOriginalButtonTexts(); - } - - // First, set the width of _panelHoldingToolstrip, the control holding the language menu, help menu, - // and possibly zoom control. It must be wide enough to display its content. In stages Full and 1, - // it is also not less than original width. - int desiredToolStripPanelWidth = Math.Max( - _toolStrip.Width + MinToolStripMargin, - _currentShrinkage <= Shrinkage.Stage1 ? _originalToolStripPanelWidth : 0 - ); - if (desiredToolStripPanelWidth != _panelHoldingToolStrip.Width) - { - _panelHoldingToolStrip.Width = desiredToolStripPanelWidth; - AlignTopRightPanels(); - } - - switch (_currentShrinkage) - { - default: - // Shrinkage.FullSize - if (Width < STAGE_1) - { - // shrink to stage 1 - _currentShrinkage = Shrinkage.Stage1; - ShrinkToStage1(); - - // It is possible that we are jumping from FullScreen to a 'remembered' - // smaller screen size, so test for all of them! - if (Width < STAGE_2) - { - _currentShrinkage = Shrinkage.Stage2; - ShrinkToStage2(); - - if (Width < STAGE_3) - { - _currentShrinkage = Shrinkage.Stage3; - ShrinkToStage3(); - } - } - } - break; - case Shrinkage.Stage1: - if (Width >= STAGE_1) - { - // grow back to unshrunk - _currentShrinkage = Shrinkage.FullSize; - GrowToFullSize(); - break; - } - if (Width < STAGE_2) - { - // shrink to stage 2 - _currentShrinkage = Shrinkage.Stage2; - ShrinkToStage2(); - } - break; - case Shrinkage.Stage2: - if (Width >= STAGE_2) - { - // grow back to stage 1 - _currentShrinkage = Shrinkage.Stage1; - GrowToStage1(); - break; - } - if (Width < STAGE_3) - { - // shrink to stage 3 - _currentShrinkage = Shrinkage.Stage3; - ShrinkToStage3(); - } - break; - case Shrinkage.Stage3: - if (Width >= STAGE_3) - { - // grow back to stage 2 - _currentShrinkage = Shrinkage.Stage2; - GrowToStage2(); - } - break; - } - } - - private void SaveOriginalWidthValues() - { - _originalToolStripPanelWidth = _panelHoldingToolStrip.Width; - _originalToolSpecificPanelHorizPos = _toolSpecificPanel.Location.X; - _originalUiMenuWidth = _uiLanguageMenu.Width; - _stage1SpaceSaved = 0; - _stage2SpaceSaved = 0; - } - - private void SaveOriginalButtonTexts() - { - _originalHelpText = _helpMenu.Text; - _originalUiLanguageSelection = _uiLanguageMenu.Text; - } - - // Stage 1 removes the space we initially leave in edit and publish views to the left of the - // tool-specific buttons. (It has no visible effect in collection view, where the tool-specific buttons - // are right-aligned.) - private void ShrinkToStage1() - { - // Calculate right edge of tabs and move _toolSpecificPanel over to it - var rightEdge = _publishTab.Bounds.Right + 5; - if (_originalToolSpecificPanelHorizPos <= rightEdge) - return; - _stage1SpaceSaved = _originalToolSpecificPanelHorizPos - rightEdge; - var currentToolPanelVert = _toolSpecificPanel.Location.Y; - _toolSpecificPanel.Location = new Point(rightEdge, currentToolPanelVert); - AlignTopRightPanels(); - } - - /// - /// Keep the _panelHoldingToolStrip in the top right and the _toolSpecificPanel's right edge aligned with it. - /// Normally during resize this happens automatically since both are anchored Right. But when we fiddle with - /// the width or position of one of them we need to straighten things out. - /// - void AlignTopRightPanels() - { - _panelHoldingToolStrip.Left = this.Width - _panelHoldingToolStrip.Width; // align this panel on the right. - _toolSpecificPanel.Width = _panelHoldingToolStrip.Left - _toolSpecificPanel.Left; - } - - private void GrowToFullSize() - { - // revert _toolSpecificPanel to its original location - _toolSpecificPanel.Location = new Point( - _originalToolSpecificPanelHorizPos, - _toolSpecificPanel.Location.Y - ); - AlignTopRightPanels(); - _stage1SpaceSaved = 0; - } - - /// - /// Adjust buttons for the current Locale. In particular the Help button may be an icon or a translation. - /// - void AdjustButtonTextsForLocale() - { - if (_originalHelpImage == null) - _originalHelpImage = _helpMenu.Image; - var helpText = LocalizationManager.GetString("HelpMenu.Help Menu", "?"); - if ( - helpText == "?" - || new[] { "en", "fr", "de", "es" }.Contains(LocalizationManager.UILanguageId) - ) - { - _helpMenu.Text = ""; - _helpMenu.Image = _originalHelpImage; - } - else - { - _helpMenu.Text = helpText; - _helpMenu.Image = null; - } - } - - // Currently stage 2 removes the space between the right-hand toolstrip and the tool-specific controls, - // by shrinking _panelHoldingToolStrip. - private void ShrinkToStage2() - { - _panelHoldingToolStrip.Width = _toolStrip.Width + MinToolStripMargin; - AlignTopRightPanels(); - _stage2SpaceSaved = _originalToolStripPanelWidth - _panelHoldingToolStrip.Width; - } - - private void GrowToStage1() - { - _panelHoldingToolStrip.Width = _originalToolStripPanelWidth; - AlignTopRightPanels(); - _stage2SpaceSaved = 0; - } - - // Stage 3 hides the right-hand toolstrip altogether. - private void ShrinkToStage3() - { - // Extreme measures for really small screens - _panelHoldingToolStrip.Visible = false; - _toolSpecificPanel.Width = Width - _toolSpecificPanel.Left; - } - - private void GrowToStage2() - { - _panelHoldingToolStrip.Visible = true; - AlignTopRightPanels(); - } - - #endregion } public class NoBorderToolStripRenderer : ToolStripProfessionalRenderer diff --git a/src/BloomExe/Workspace/ZoomControl.Designer.cs b/src/BloomExe/Workspace/ZoomControl.Designer.cs deleted file mode 100644 index 2b64d18b15b4..000000000000 --- a/src/BloomExe/Workspace/ZoomControl.Designer.cs +++ /dev/null @@ -1,100 +0,0 @@ -namespace Bloom.Workspace -{ - partial class ZoomControl - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Component Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this._minusButton = new System.Windows.Forms.Button(); - this._plusButton = new System.Windows.Forms.Button(); - this._percentLabel = new System.Windows.Forms.Label(); - this.SuspendLayout(); - // - // _minusButton - // - this._minusButton.BackColor = System.Drawing.Color.Transparent; - this._minusButton.FlatAppearance.BorderSize = 0; - this._minusButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this._minusButton.Font = new System.Drawing.Font("Segoe UI", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this._minusButton.Location = new System.Drawing.Point(0, -8); - this._minusButton.Margin = new System.Windows.Forms.Padding(0); - this._minusButton.Name = "_minusButton"; - this._minusButton.Size = new System.Drawing.Size(17, 38); - this._minusButton.TabIndex = 0; - this._minusButton.Text = "–"; - this._minusButton.TextAlign = System.Drawing.ContentAlignment.TopCenter; - this._minusButton.UseVisualStyleBackColor = false; - this._minusButton.Click += new System.EventHandler(this._minusButton_Click); - // - // _plusButton - // - this._plusButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this._plusButton.BackColor = System.Drawing.Color.Transparent; - this._plusButton.FlatAppearance.BorderSize = 0; - this._plusButton.FlatStyle = System.Windows.Forms.FlatStyle.Flat; - this._plusButton.Font = new System.Drawing.Font("Segoe UI", 14.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this._plusButton.Location = new System.Drawing.Point(58, -8); - this._plusButton.Name = "_plusButton"; - this._plusButton.Size = new System.Drawing.Size(19, 38); - this._plusButton.TabIndex = 1; - this._plusButton.Text = "+"; - this._plusButton.TextAlign = System.Drawing.ContentAlignment.TopCenter; - this._plusButton.UseVisualStyleBackColor = false; - this._plusButton.Click += new System.EventHandler(this._plusButton_Click); - // - // _percentLabel - // - this._percentLabel.Anchor = System.Windows.Forms.AnchorStyles.Top; - this._percentLabel.Font = new System.Drawing.Font("Segoe UI", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this._percentLabel.Location = new System.Drawing.Point(20, 3); - this._percentLabel.Name = "_percentLabel"; - this._percentLabel.Size = new System.Drawing.Size(35, 13); - this._percentLabel.TabIndex = 2; - this._percentLabel.Text = "100%"; - this._percentLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // ZoomControl - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.Controls.Add(this._percentLabel); - this.Controls.Add(this._plusButton); - this.Controls.Add(this._minusButton); - this.MaximumSize = new System.Drawing.Size(80, 17); - this.MinimumSize = new System.Drawing.Size(80, 17); - this.Name = "ZoomControl"; - this.Size = new System.Drawing.Size(80, 17); - this.ResumeLayout(false); - - } - - #endregion - - private System.Windows.Forms.Button _minusButton; - private System.Windows.Forms.Button _plusButton; - private System.Windows.Forms.Label _percentLabel; - } -} diff --git a/src/BloomExe/Workspace/ZoomControl.cs b/src/BloomExe/Workspace/ZoomControl.cs deleted file mode 100644 index eb400263e9c8..000000000000 --- a/src/BloomExe/Workspace/ZoomControl.cs +++ /dev/null @@ -1,60 +0,0 @@ -using System; -using System.Drawing; -using System.Windows.Forms; - -namespace Bloom.Workspace -{ - public partial class ZoomControl : UserControl - { - public const int kMinimumZoom = 30; // 30% - 300% matches FireFox - public const int kMaximumZoom = 300; - - private int _zoom; - - public ZoomControl() - { - InitializeComponent(); - if (SIL.PlatformUtilities.Platform.IsLinux) - { - // Work around a bug in Mono 5 (and Mono 6?). - // See https://issues.bloomlibrary.org/youtrack/issue/BL-8360. - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.None; - this._minusButton.Location = new System.Drawing.Point(0, -8); // restore original locations - this._percentLabel.Location = new System.Drawing.Point(20, 4); - this._plusButton.Location = new System.Drawing.Point(58, -8); - } - Zoom = 100; - _plusButton.ForeColor = Color.FromKnownColor(KnownColor.MenuText); - _plusButton.FlatAppearance.MouseOverBackColor = SystemColors.GradientActiveCaption; - _minusButton.ForeColor = Color.FromKnownColor(KnownColor.MenuText); - _minusButton.FlatAppearance.MouseOverBackColor = SystemColors.GradientActiveCaption; - _percentLabel.ForeColor = Color.FromKnownColor(KnownColor.MenuText); - } - - public int Zoom - { - get { return _zoom; } - set - { - var newValue = Math.Min(Math.Max(value, kMinimumZoom), kMaximumZoom); - if (newValue == _zoom) - return; - _zoom = newValue; - _percentLabel.Text = _zoom + @"%"; - ZoomChanged?.Invoke(this, new EventArgs()); - } - } - - public event EventHandler ZoomChanged; - - private void _minusButton_Click(object sender, EventArgs e) - { - Zoom = Zoom - 10; - } - - private void _plusButton_Click(object sender, EventArgs e) - { - Zoom = Zoom + 10; - } - } -} diff --git a/src/BloomExe/Workspace/ZoomControl.resx b/src/BloomExe/Workspace/ZoomControl.resx deleted file mode 100644 index 1af7de150c99..000000000000 --- a/src/BloomExe/Workspace/ZoomControl.resx +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/src/BloomExe/Workspace/ZoomModel.cs b/src/BloomExe/Workspace/ZoomModel.cs new file mode 100644 index 000000000000..44e52e238f28 --- /dev/null +++ b/src/BloomExe/Workspace/ZoomModel.cs @@ -0,0 +1,27 @@ +using System; + +namespace Bloom.Workspace +{ + public class ZoomModel + { + public const int kMinimumZoom = 30; // 30% - 300% matches FireFox + public const int kMaximumZoom = 300; + + private int _zoom = 100; + + public int Zoom + { + get { return _zoom; } + set + { + var newValue = Math.Min(Math.Max(value, kMinimumZoom), kMaximumZoom); + if (newValue == _zoom) + return; + _zoom = newValue; + ZoomChanged?.Invoke(this, EventArgs.Empty); + } + } + + public event EventHandler ZoomChanged; + } +} diff --git a/src/BloomExe/web/ReactControl.cs b/src/BloomExe/web/ReactControl.cs index 7df0b0090de2..9bd400014874 100644 --- a/src/BloomExe/web/ReactControl.cs +++ b/src/BloomExe/web/ReactControl.cs @@ -55,6 +55,15 @@ public string JavascriptBundleName private Browser _browser; + protected override void OnBackColorChanged(EventArgs e) + { + base.OnBackColorChanged(e); + var htmlColor = MiscUtils.ColorToHtmlCode(BackColor); + _browser?.RunJavascriptFireAndForget( + $"document.body.style.backgroundColor = '{htmlColor}';" + ); + } + private void ReactControl_Load(object sender, System.EventArgs e) { if (this.DesignModeAtAll()) @@ -219,6 +228,10 @@ private TempFile MakeTempFile() "teamCollectionSettingsBundle", "/teamCollection/TeamCollectionSettingsPanel.entry.tsx" }, + { + "workspaceTopRightControlsBundle", + "/react_components/TopBar/workspaceTopRightControls/WorkspaceTopRightControls.entry.tsx" + }, }; string viteModulePath = null; var useViteDev = ShouldUseViteDev(() => diff --git a/src/BloomExe/web/controllers/WorkspaceApi.cs b/src/BloomExe/web/controllers/WorkspaceApi.cs index f42342e6d279..e0d8e2d4a9db 100644 --- a/src/BloomExe/web/controllers/WorkspaceApi.cs +++ b/src/BloomExe/web/controllers/WorkspaceApi.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using System.Windows.Forms; using Bloom.Api; using Bloom.Workspace; @@ -34,6 +30,23 @@ public void RegisterWithApiHandler(BloomApiHandler apiHandler) HandleShowLegacySettingsDialog, true ); + + apiHandler.RegisterEndpointHandler( + "workspace/topRight/openLanguageMenu", + HandleOpenLanguageMenu, + true + ); + apiHandler.RegisterEndpointHandler( + "workspace/topRight/openHelpMenu", + HandleOpenHelpMenu, + true + ); + apiHandler.RegisterEndpointHandler( + "workspace/topRight/uiLanguageLabel", + HandleGetUiLanguageLabel, + true + ); + apiHandler.RegisterEndpointHandler("workspace/topRight/zoom", HandleZoom, true); } private void HandleOpenOrCreateCollection(ApiRequest request) @@ -59,5 +72,36 @@ private void HandleShowLegacySettingsDialog(ApiRequest request) request.PostSucceeded(); } + + private void HandleGetUiLanguageLabel(ApiRequest request) + { + request.ReplyWithJson(WorkspaceView.GetCurrentUiLanguageLabel()); + } + + private void HandleOpenLanguageMenu(ApiRequest request) + { + WorkspaceView.ShowUiLanguageMenu(); + request.PostSucceeded(); + } + + private void HandleOpenHelpMenu(ApiRequest request) + { + WorkspaceView.ShowHelpMenu(); + request.PostSucceeded(); + } + + private void HandleZoom(ApiRequest request) + { + if (request.HttpMethod == HttpMethods.Get) + { + request.ReplyWithJson(WorkspaceView.GetZoomInfo()); + return; + } + + var data = request.RequiredPostDynamic(); + int zoom = Convert.ToInt32(data.zoom); + WorkspaceView.SetZoom(zoom); + request.PostSucceeded(); + } } }