diff --git a/OpenRA.Game/Graphics/Viewport.cs b/OpenRA.Game/Graphics/Viewport.cs index 1784708d8c..217fcb8127 100644 --- a/OpenRA.Game/Graphics/Viewport.cs +++ b/OpenRA.Game/Graphics/Viewport.cs @@ -54,8 +54,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 @@ -65,7 +72,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; @@ -75,6 +83,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 cce417f2b8..cd724f0c5a 100644 --- a/OpenRA.Game/Settings.cs +++ b/OpenRA.Game/Settings.cs @@ -175,6 +175,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 93ac4c95f0..de1f2b283b 100644 --- a/OpenRA.Mods.Common/Widgets/DropDownButtonWidget.cs +++ b/OpenRA.Mods.Common/Widgets/DropDownButtonWidget.cs @@ -27,6 +27,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 bfb3aad68f..62b3191d78 100644 --- a/OpenRA.Mods.Common/Widgets/EditorViewportControllerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/EditorViewportControllerWidget.cs @@ -70,8 +70,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 c0367232e9..9ed49b25cc 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/Editor/MapEditorLogic.cs @@ -36,13 +36,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; @@ -50,9 +59,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 => { @@ -60,9 +69,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 d0b5cad6ee..554c6a45c3 100644 --- a/OpenRA.Mods.Common/Widgets/Logic/SettingsLogic.cs +++ b/OpenRA.Mods.Common/Widgets/Logic/SettingsLogic.cs @@ -382,6 +382,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"); @@ -401,6 +402,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"); @@ -564,6 +569,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()) { @@ -639,6 +646,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 3d2b587420..4778ee24d3 100644 --- a/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs +++ b/OpenRA.Mods.Common/Widgets/ViewportControllerWidget.cs @@ -190,8 +190,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 43e788b99d..7fc8106265 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 a9c16b85ed..9447c2e8a2 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