diff --git a/OpenRA.Game/Graphics/Viewport.cs b/OpenRA.Game/Graphics/Viewport.cs index 6052ff3a76..40ba91a10b 100644 --- a/OpenRA.Game/Graphics/Viewport.cs +++ b/OpenRA.Game/Graphics/Viewport.cs @@ -53,8 +53,15 @@ namespace OpenRA.Graphics ProjectedCellRegion allCells; bool allCellsDirty = true; + readonly float[] availableZoomSteps = new[] { 2f, 1f, 0.5f, 0.25f }; float zoom = 1f; + + public float[] AvailableZoomSteps + { + get { return availableZoomSteps; } + } + public float Zoom { get @@ -64,7 +71,8 @@ namespace OpenRA.Graphics set { - zoom = value; + var newValue = ClosestTo(AvailableZoomSteps, value); + zoom = newValue; viewportSize = (1f / zoom * new float2(Game.Renderer.Resolution)).ToInt2(); cellsDirty = true; allCellsDirty = true; @@ -74,6 +82,23 @@ namespace OpenRA.Graphics public static int TicksSinceLastMove = 0; public static int2 LastMousePos; + float ClosestTo(float[] collection, float target) + { + var closestValue = collection.First(); + var subtractResult = Math.Abs(closestValue - target); + + foreach (var element in collection) + { + if (Math.Abs(element - target) < subtractResult) + { + subtractResult = Math.Abs(element - target); + closestValue = element; + } + } + + return closestValue; + } + public ScrollDirection GetBlockedDirections() { var ret = ScrollDirection.None; diff --git a/OpenRA.Game/Settings.cs b/OpenRA.Game/Settings.cs index f69a9ee4d7..0a7057db8a 100644 --- a/OpenRA.Game/Settings.cs +++ b/OpenRA.Game/Settings.cs @@ -174,6 +174,9 @@ namespace OpenRA public bool AllowDownloading = true; public string MapRepository = "http://resource.openra.net/map/"; + public bool AllowZoom = true; + public Modifiers ZoomModifier = Modifiers.Ctrl; + public bool FetchNews = true; public string NewsUrl = "http://www.openra.net/gamenews"; } diff --git a/OpenRA.Mods.Common/Widgets/DropDownButtonWidget.cs b/OpenRA.Mods.Common/Widgets/DropDownButtonWidget.cs index cd87f45768..b5f2079033 100644 --- a/OpenRA.Mods.Common/Widgets/DropDownButtonWidget.cs +++ b/OpenRA.Mods.Common/Widgets/DropDownButtonWidget.cs @@ -26,6 +26,7 @@ namespace OpenRA.Mods.Common.Widgets Widget panelRoot; public string PanelRoot; + public string SelectedItem; [ObjectCreator.UseCtor] public DropDownButtonWidget(Ruleset modRules) diff --git a/OpenRA.Mods.Common/Widgets/EditorViewportControllerWidget.cs b/OpenRA.Mods.Common/Widgets/EditorViewportControllerWidget.cs index 350e90b54f..a90cfd75e3 100644 --- a/OpenRA.Mods.Common/Widgets/EditorViewportControllerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/EditorViewportControllerWidget.cs @@ -69,8 +69,29 @@ namespace OpenRA.Mods.Common.Widgets tooltipContainer.Value.RemoveTooltip(); } + void Zoom(int amount) + { + var zoomSteps = worldRenderer.Viewport.AvailableZoomSteps; + var currentZoom = worldRenderer.Viewport.Zoom; + + var nextIndex = zoomSteps.IndexOf(currentZoom) - amount; + if (nextIndex < 0 || nextIndex >= zoomSteps.Length) + return; + + var zoom = zoomSteps[nextIndex]; + Parent.Get("ZOOM_BUTTON").SelectedItem = zoom.ToString(); + worldRenderer.Viewport.Zoom = zoom; + } + public override bool HandleMouseInput(MouseInput mi) { + if (mi.Event == MouseInputEvent.Scroll && + Game.Settings.Game.AllowZoom && mi.Modifiers.HasModifier(Game.Settings.Game.ZoomModifier)) + { + Zoom(mi.ScrollDelta); + return true; + } + if (CurrentBrush.HandleMouseInput(mi)) return true; diff --git a/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorLogic.cs index 30c5c3c888..44390cd213 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorLogic.cs @@ -35,13 +35,22 @@ namespace OpenRA.Mods.Common.Widgets.Logic var zoomDropdown = widget.GetOrNull("ZOOM_BUTTON"); if (zoomDropdown != null) { - var selectedZoom = Game.Settings.Graphics.PixelDouble ? 2f : 1f; - var selectedLabel = selectedZoom.ToString(); + var selectedZoom = (Game.Settings.Graphics.PixelDouble ? 2f : 1f).ToString(); + + zoomDropdown.SelectedItem = selectedZoom; Func setupItem = (zoom, itemTemplate) => { - var item = ScrollItemWidget.Setup(itemTemplate, - () => selectedZoom == zoom, - () => { worldRenderer.Viewport.Zoom = selectedZoom = zoom; selectedLabel = zoom.ToString(); }); + var item = ScrollItemWidget.Setup( + itemTemplate, + () => + { + return float.Parse(zoomDropdown.SelectedItem) == zoom; + }, + () => + { + zoomDropdown.SelectedItem = selectedZoom = zoom.ToString(); + worldRenderer.Viewport.Zoom = float.Parse(selectedZoom); + }); var label = zoom.ToString(); item.Get("LABEL").GetText = () => label; @@ -49,9 +58,9 @@ namespace OpenRA.Mods.Common.Widgets.Logic return item; }; - var options = new[] { 2f, 1f, 0.5f, 0.25f }; + var options = worldRenderer.Viewport.AvailableZoomSteps; zoomDropdown.OnMouseDown = _ => zoomDropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 150, options, setupItem); - zoomDropdown.GetText = () => selectedLabel; + zoomDropdown.GetText = () => zoomDropdown.SelectedItem; zoomDropdown.GetKey = _ => Game.Settings.Keys.TogglePixelDoubleKey; zoomDropdown.OnKeyPress = e => { @@ -59,9 +68,11 @@ namespace OpenRA.Mods.Common.Widgets.Logic if (key != Game.Settings.Keys.TogglePixelDoubleKey) return; - var selected = (options.IndexOf(selectedZoom) + 1) % options.Length; - worldRenderer.Viewport.Zoom = selectedZoom = options[selected]; - selectedLabel = selectedZoom.ToString(); + var selected = (options.IndexOf(float.Parse(selectedZoom)) + 1) % options.Length; + var zoom = options[selected]; + worldRenderer.Viewport.Zoom = zoom; + selectedZoom = zoom.ToString(); + zoomDropdown.SelectedItem = zoom.ToString(); }; } diff --git a/OpenRA.Mods.Common/Widgets/Logic/SettingsLogic.cs b/OpenRA.Mods.Common/Widgets/Logic/SettingsLogic.cs index 2d3b5b69c9..ee2e0e4027 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/SettingsLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/SettingsLogic.cs @@ -381,6 +381,7 @@ namespace OpenRA.Mods.Common.Widgets.Logic BindCheckboxPref(panel, "CLASSICORDERS_CHECKBOX", gs, "UseClassicMouseStyle"); BindCheckboxPref(panel, "EDGESCROLL_CHECKBOX", gs, "ViewportEdgeScroll"); BindCheckboxPref(panel, "LOCKMOUSE_CHECKBOX", gs, "LockMouseWindow"); + BindCheckboxPref(panel, "ALLOW_ZOOM_CHECKBOX", gs, "AllowZoom"); BindSliderPref(panel, "SCROLLSPEED_SLIDER", gs, "ViewportEdgeScrollStep"); BindSliderPref(panel, "UI_SCROLLSPEED_SLIDER", gs, "UIScrollSpeed"); @@ -400,6 +401,10 @@ namespace OpenRA.Mods.Common.Widgets.Logic mouseScrollDropdown.OnMouseDown = _ => ShowMouseScrollDropdown(mouseScrollDropdown, gs); mouseScrollDropdown.GetText = () => gs.MouseScroll.ToString(); + var zoomModifierDropdown = panel.Get("ZOOM_MODIFIER"); + zoomModifierDropdown.OnMouseDown = _ => ShowZoomModifierDropdown(zoomModifierDropdown, gs); + zoomModifierDropdown.GetText = () => gs.ZoomModifier.ToString(); + var hotkeyList = panel.Get("HOTKEY_LIST"); hotkeyList.Layout = new GridLayout(hotkeyList); var hotkeyHeader = hotkeyList.Get("HEADER"); @@ -562,6 +567,8 @@ namespace OpenRA.Mods.Common.Widgets.Logic gs.ViewportEdgeScroll = dgs.ViewportEdgeScroll; gs.ViewportEdgeScrollStep = dgs.ViewportEdgeScrollStep; gs.UIScrollSpeed = dgs.UIScrollSpeed; + gs.AllowZoom = dgs.AllowZoom; + gs.ZoomModifier = dgs.ZoomModifier; foreach (var f in ks.GetType().GetFields()) { @@ -637,6 +644,29 @@ namespace OpenRA.Mods.Common.Widgets.Logic return true; } + static bool ShowZoomModifierDropdown(DropDownButtonWidget dropdown, GameSettings s) + { + var options = new Dictionary() + { + { "Alt", Modifiers.Alt }, + { "Ctrl", Modifiers.Ctrl }, + { "Meta", Modifiers.Meta }, + { "Shift", Modifiers.Shift }, + }; + + Func setupItem = (o, itemTemplate) => + { + var item = ScrollItemWidget.Setup(itemTemplate, + () => s.ZoomModifier == options[o], + () => s.ZoomModifier = options[o]); + item.Get("LABEL").GetText = () => o; + return item; + }; + + dropdown.ShowDropDown("LABEL_DROPDOWN_TEMPLATE", 500, options.Keys, setupItem); + return true; + } + bool ShowAudioDeviceDropdown(DropDownButtonWidget dropdown, SoundDevice[] devices) { var i = 0; diff --git a/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs b/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs index 99fddc6dc3..1b60de78c3 100644 --- a/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs @@ -189,8 +189,41 @@ namespace OpenRA.Mods.Common.Widgets } } + bool IsZoomAllowed(float zoom) + { + return world.IsGameOver || zoom >= 1.0f || world.IsReplay || world.LocalPlayer == null || world.LocalPlayer.Spectating; + } + + void Zoom(int direction) + { + var zoomSteps = worldRenderer.Viewport.AvailableZoomSteps; + var currentZoom = worldRenderer.Viewport.Zoom; + var nextIndex = zoomSteps.IndexOf(currentZoom); + + if (direction < 0) + nextIndex++; + else + nextIndex--; + + if (nextIndex < 0 || nextIndex >= zoomSteps.Count()) + return; + + var zoom = zoomSteps.ElementAt(nextIndex); + if (!IsZoomAllowed(zoom)) + return; + + worldRenderer.Viewport.Zoom = zoom; + } + public override bool HandleMouseInput(MouseInput mi) { + if (mi.Event == MouseInputEvent.Scroll && + Game.Settings.Game.AllowZoom && mi.Modifiers.HasModifier(Game.Settings.Game.ZoomModifier)) + { + Zoom(mi.ScrollDelta); + return true; + } + var scrolltype = Game.Settings.Game.MouseScroll; if (scrolltype == MouseScrollType.Disabled) return false; diff --git a/OpenRA.Platforms.Default/Sdl2Input.cs b/OpenRA.Platforms.Default/Sdl2Input.cs index 3634389255..fc7c6e96e7 100644 --- a/OpenRA.Platforms.Default/Sdl2Input.cs +++ b/OpenRA.Platforms.Default/Sdl2Input.cs @@ -124,7 +124,7 @@ namespace OpenRA.Platforms.Default int x, y; SDL.SDL_GetMouseState(out x, out y); scrollDelta = e.wheel.y; - inputHandler.OnMouseInput(new MouseInput(MouseInputEvent.Scroll, MouseButton.None, scrollDelta, new int2(x, y), Modifiers.None, 0)); + inputHandler.OnMouseInput(new MouseInput(MouseInputEvent.Scroll, MouseButton.None, scrollDelta, new int2(x, y), mods, 0)); break; } diff --git a/mods/cnc/chrome/settings.yaml b/mods/cnc/chrome/settings.yaml index c6747644cd..5861671665 100644 --- a/mods/cnc/chrome/settings.yaml +++ b/mods/cnc/chrome/settings.yaml @@ -353,30 +353,52 @@ Container@SETTINGS_PANEL: Height: 25 Font: Regular Text: Enabled - Checkbox@EDGESCROLL_CHECKBOX: + Checkbox@ALLOW_ZOOM_CHECKBOX: X: 15 Y: 70 Width: 130 Height: 20 Font: Regular + Text: Allow zoom + Label@ZOOM_MODIFIER_LABEL: + X: PARENT_RIGHT - WIDTH - 120 + Y: 68 + Width: 160 + Height: 20 + Font: Regular + Text: Modifier used to zoom: + Align: Right + DropDownButton@ZOOM_MODIFIER: + X: PARENT_RIGHT - WIDTH - 15 + Y: 68 + Width: 100 + Height: 25 + Font: Regular + Text: Alt + Checkbox@EDGESCROLL_CHECKBOX: + X: 15 + Y: 100 + Width: 130 + Height: 20 + Font: Regular Text: Edge Scrolling Checkbox@LOCKMOUSE_CHECKBOX: X: 15 - Y: 100 + Y: 130 Width: 130 Height: 20 Font: Regular Text: Lock mouse to window Label@SCROLL_SPEED_LABEL: X: PARENT_RIGHT - WIDTH - 270 - Y: 67 + Y: 97 Width: 95 Height: 25 Text: Scroll Speed: Align: Right Slider@SCROLLSPEED_SLIDER: X: PARENT_RIGHT - WIDTH - 15 - Y: 73 + Y: 103 Width: 250 Height: 20 Ticks: 5 @@ -384,32 +406,32 @@ Container@SETTINGS_PANEL: MaximumValue: 50 Label@UI_SCROLL_SPEED_LABEL: X: PARENT_RIGHT - WIDTH - 270 - Y: 97 + Y: 127 Width: 95 Height: 25 Text: UI Scroll Speed: Align: Right Slider@UI_SCROLLSPEED_SLIDER: X: PARENT_RIGHT - WIDTH - 15 - Y: 103 + Y: 133 Width: 250 Height: 20 Ticks: 5 MinimumValue: 1 MaximumValue: 100 Label@HOTKEYS_TITLE: - Y: 135 + Y: 165 Width: PARENT_RIGHT Font: Bold Text: Hotkeys Align: Center ScrollPanel@HOTKEY_LIST: X: 15 - Y: 155 + Y: 185 Width: 560 TopBottomSpacing: 4 ItemSpacing: 4 - Height: 190 + Height: 160 Children: ScrollItem@HEADER: Width: 528 diff --git a/mods/ra/chrome/settings.yaml b/mods/ra/chrome/settings.yaml index f495390ac9..aea4153d69 100644 --- a/mods/ra/chrome/settings.yaml +++ b/mods/ra/chrome/settings.yaml @@ -357,30 +357,52 @@ Background@SETTINGS_PANEL: Height: 25 Font: Regular Text: Enabled - Checkbox@EDGESCROLL_CHECKBOX: + Checkbox@ALLOW_ZOOM_CHECKBOX: X: 15 Y: 70 Width: 130 Height: 20 Font: Regular + Text: Allow zoom + Label@ZOOM_MODIFIER_LABEL: + X: PARENT_RIGHT - WIDTH - 120 + Y: 68 + Width: 160 + Height: 20 + Font: Regular + Text: Modifier used to zoom: + Align: Right + DropDownButton@ZOOM_MODIFIER: + X: PARENT_RIGHT - WIDTH - 15 + Y: 68 + Width: 100 + Height: 25 + Font: Regular + Text: Alt + Checkbox@EDGESCROLL_CHECKBOX: + X: 15 + Y: 100 + Width: 130 + Height: 20 + Font: Regular Text: Edge Scrolling Checkbox@LOCKMOUSE_CHECKBOX: X: 15 - Y: 100 + Y: 130 Width: 130 Height: 20 Font: Regular Text: Lock mouse to window Label@SCROLL_SPEED_LABEL: X: PARENT_RIGHT - WIDTH - 270 - Y: 67 + Y: 97 Width: 95 Height: 25 Text: Scroll Speed: Align: Right Slider@SCROLLSPEED_SLIDER: X: PARENT_RIGHT - WIDTH - 15 - Y: 73 + Y: 103 Width: 250 Height: 20 Ticks: 5 @@ -388,32 +410,32 @@ Background@SETTINGS_PANEL: MaximumValue: 50 Label@UI_SCROLL_SPEED_LABEL: X: PARENT_RIGHT - WIDTH - 270 - Y: 97 + Y: 127 Width: 95 Height: 25 Text: UI Scroll Speed: Align: Right Slider@UI_SCROLLSPEED_SLIDER: X: PARENT_RIGHT - WIDTH - 15 - Y: 103 + Y: 133 Width: 250 Height: 20 Ticks: 5 MinimumValue: 1 MaximumValue: 100 Label@HOTKEYS_TITLE: - Y: 135 + Y: 165 Width: PARENT_RIGHT Font: Bold Text: Hotkeys Align: Center ScrollPanel@HOTKEY_LIST: X: 15 - Y: 155 + Y: 185 Width: 560 TopBottomSpacing: 4 ItemSpacing: 4 - Height: 190 + Height: 160 Children: ScrollItem@HEADER: BaseName: scrollheader