diff --git a/Source/Plugins/Tilemaps/Editor/CamViewStates/TilemapEditorCamViewState.cs b/Source/Plugins/Tilemaps/Editor/CamViewStates/TilemapEditorCamViewState.cs index 8fb005e23..2b9173a87 100644 --- a/Source/Plugins/Tilemaps/Editor/CamViewStates/TilemapEditorCamViewState.cs +++ b/Source/Plugins/Tilemaps/Editor/CamViewStates/TilemapEditorCamViewState.cs @@ -605,7 +605,7 @@ private void DeselectToolInInspector() if (this.selectedTool == null) return; DualityEditorApp.Deselect(this, new ObjectSelection(new object[] { this.selectedTool.Settings })); } - + protected override string UpdateStatusText() { // Display which Tilemap we're currently using @@ -630,6 +630,7 @@ protected override void OnEnterState() DualityEditorApp.ObjectPropertyChanged += this.DualityEditorApp_ObjectPropertyChanged; DualityEditorApp.UpdatingEngine += this.DualityEditorApp_UpdatingEngine; Scene.Entered += this.Scene_Entered; + TilemapsEditorPlugin.Instance.TileDrawingSourceChanged += this.TilemapToolSourcePalette_SelectedAreaChanged; // Initial update this.UpdateTilemapToolButtons(); @@ -755,9 +756,9 @@ protected override void OnMouseUp(MouseEventArgs e) protected override void OnKeyDown(KeyEventArgs e) { base.OnKeyDown(e); - + // Hotkeys for switching the currently selected tilemap - if (e.KeyCode == Keys.Up || e.KeyCode == Keys.Down) + if (e.KeyCode == Keys.PageUp || e.KeyCode == Keys.PageDown) { Tilemap[] visibleTilemaps = this.QueryVisibleTilemapRenderers() @@ -770,9 +771,9 @@ protected override void OnKeyDown(KeyEventArgs e) if (visibleTilemaps.Length > 0) { - if (e.KeyCode == Keys.Down) + if (e.KeyCode == Keys.PageDown) selectedIndex = (selectedIndex == -1) ? (visibleTilemaps.Length - 1) : Math.Min(selectedIndex + 1, visibleTilemaps.Length - 1); - else if (e.KeyCode == Keys.Up) + else if (e.KeyCode == Keys.PageUp) selectedIndex = (selectedIndex == -1) ? 0 : Math.Max(selectedIndex - 1, 0); Tilemap newSelection = visibleTilemaps[selectedIndex]; @@ -782,7 +783,7 @@ protected override void OnKeyDown(KeyEventArgs e) e.Handled = true; return; } - else if (e.KeyCode == Keys.Left || e.KeyCode == Keys.Right) + else if (e.KeyCode == Keys.Escape) { DualityEditorApp.Deselect(this, ObjectSelection.Category.GameObjCmp); e.Handled = true; @@ -805,11 +806,13 @@ protected override void OnKeyDown(KeyEventArgs e) break; } } + TilemapsEditorPlugin.Instance.PeekTilePalette().RaiseKeyDownEvent(e); } protected override void OnKeyUp(KeyEventArgs e) { base.OnKeyUp(e); - + TilemapsEditorPlugin.Instance.PeekTilePalette().RaiseKeyUpEvent(e); + if (this.overrideTool != null && this.overrideTool.OverrideKey == e.KeyCode) { this.OverrideTool = null; @@ -1041,6 +1044,12 @@ private void Scene_Entered(object sender, EventArgs e) { this.UpdateActionToolButtons(); } + private void TilemapToolSourcePalette_SelectedAreaChanged(object sender, EventArgs e) + { + this.activeTool.UpdatePreview(); + this.Invalidate(); + } + private void actionToolButton_Click(object sender, EventArgs e) { TilemapActionEntry clickedEntry = this.actions.FirstOrDefault(entry => entry.ToolButton == sender); diff --git a/Source/Plugins/Tilemaps/Editor/EditorPlugin.cs b/Source/Plugins/Tilemaps/Editor/EditorPlugin.cs index 6c6e4d5ff..60bd2c5bb 100644 --- a/Source/Plugins/Tilemaps/Editor/EditorPlugin.cs +++ b/Source/Plugins/Tilemaps/Editor/EditorPlugin.cs @@ -187,7 +187,14 @@ public void PopTilePalette() else this.pendingLocalTilePalettes--; } - + /// + /// Gets the if there is one, returns null otherwise. + /// + public TilemapToolSourcePalette PeekTilePalette() + { + return this.tilePalette; + } + public TilesetEditor RequestTilesetEditor() { // Create a new tileset editor, if no is available right now diff --git a/Source/Plugins/Tilemaps/Editor/Modules/SourcePaletteTilesetView.cs b/Source/Plugins/Tilemaps/Editor/Modules/SourcePaletteTilesetView.cs index ad0fdf58b..bb99fa1bc 100644 --- a/Source/Plugins/Tilemaps/Editor/Modules/SourcePaletteTilesetView.cs +++ b/Source/Plugins/Tilemaps/Editor/Modules/SourcePaletteTilesetView.cs @@ -4,9 +4,10 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; - +using Duality.Input; using Duality.Resources; using Duality.Plugins.Tilemaps; +using MouseEventArgs = System.Windows.Forms.MouseEventArgs; namespace Duality.Editor.Plugins.Tilemaps @@ -21,6 +22,17 @@ public class SourcePaletteTilesetView : TilesetView private bool isUserScrolling = false; private int lastMouseX = -1; private int lastMouseY = -1; + private SelectionSide activeSelectionSide = SelectionSide.None; + + private enum SelectionSide + { + None, + Right, + Left, + Up, + Down + } + public event EventHandler SelectedAreaChanged = null; public event EventHandler SelectedAreaEditingFinished = null; @@ -76,6 +88,14 @@ public IReadOnlyGrid SelectedTiles get { return this.selectedTiles; } } + internal void RaiseKeyDownEvent(KeyEventArgs e) + { + this.OnKeyDown(e); + } + internal void RaiseKeyUpEvent(KeyEventArgs e) + { + this.OnKeyUp(e); + } protected override void OnTilesetChanged() { @@ -203,7 +223,7 @@ protected override void OnPaintTiles(PaintEventArgs e) protected override void OnMouseDown(MouseEventArgs e) { base.OnMouseDown(e); - if (e.Button == MouseButtons.Left && Control.ModifierKeys == Keys.Shift && !this.isUserScrolling) + if (e.Button == MouseButtons.Left && ModifierKeys == Keys.Shift && !this.isUserScrolling) { int tileIndex = this.PickTileIndexAt(e.X, e.Y); if (tileIndex != -1) @@ -333,6 +353,90 @@ protected override void OnMouseLeave(EventArgs e) this.InvalidateTile(this.HoveredTileIndex, 5); base.OnMouseLeave(e); } + protected override void OnKeyDown(KeyEventArgs e) + { + base.OnKeyDown(e); + + if (Control.ModifierKeys == Keys.Shift) + { + switch (e.KeyCode) + { + case Keys.Up when this.activeSelectionSide == SelectionSide.Down: + this.ShrinkSelectedArea(0, -1); + break; + case Keys.Up: + this.ExpandSelectedArea(0, -1); + this.activeSelectionSide = SelectionSide.Up; + break; + case Keys.Down when this.activeSelectionSide == SelectionSide.Up: + this.ShrinkSelectedArea(0, 1); + break; + case Keys.Down: + this.ExpandSelectedArea(0, 1); + this.activeSelectionSide = SelectionSide.Down; + break; + case Keys.Left when this.activeSelectionSide == SelectionSide.Right: + this.ShrinkSelectedArea(-1, 0); + break; + case Keys.Left: + this.ExpandSelectedArea(-1, 0); + this.activeSelectionSide = SelectionSide.Left; + break; + case Keys.Right when this.activeSelectionSide == SelectionSide.Left: + this.ShrinkSelectedArea(1, 0); + break; + case Keys.Right: + this.ExpandSelectedArea(1, 0); + this.activeSelectionSide = SelectionSide.Right; + break; + } + } + else + { + if (e.KeyCode == Keys.Up) + { + this.TranslateSelectedArea(0, -1); + } + if (e.KeyCode == Keys.Down) + { + this.TranslateSelectedArea(0, 1); + } + if (e.KeyCode == Keys.Left) + { + this.TranslateSelectedArea(-1, 0); + } + if (e.KeyCode == Keys.Right) + { + this.TranslateSelectedArea(1, 0); + } + this.activeSelectionSide = SelectionSide.None; + } + } + + protected override void OnKeyUp(KeyEventArgs e) + { + if (e.KeyCode == Keys.ShiftKey) + { + this.activeSelectionSide = SelectionSide.None; + } + } + protected override bool IsInputKey(Keys keyData) + { + switch (keyData) + { + case Keys.Right: + case Keys.Left: + case Keys.Up: + case Keys.Down: + case Keys.Shift: + case Keys.Shift | Keys.Right: + case Keys.Shift | Keys.Left: + case Keys.Shift | Keys.Up: + case Keys.Shift | Keys.Down: + return true; + } + return base.IsInputKey(keyData); + } private void UpdateSelectedTiles() { @@ -344,7 +448,7 @@ private void UpdateSelectedTiles() // none to choose from. Keep the default-initialized ones we got above. Tileset tileset = this.TargetTileset.Res; if (tileset == null) return; - + // Determine a tile rect based on the current selection inside the Tileset. Point selectedDisplayedPos = this.GetDisplayedTilePos( this.selectedArea.X, @@ -367,16 +471,87 @@ private void UpdateSelectedTiles() } } } + private void InitializeSelectedArea() + { + Rectangle rect = new Rectangle(0, 0, 1, 1); + this.SelectedArea = rect; + this.RaiseSelectedAreaEditingFinished(); + } private void RaiseSelectedAreaEditingFinished() { - if (this.SelectedAreaEditingFinished != null) - this.SelectedAreaEditingFinished(this, EventArgs.Empty); + this.SelectedAreaEditingFinished?.Invoke(this, EventArgs.Empty); } private void RaiseSelectedAreaChanged() { - if (this.SelectedAreaChanged != null) - this.SelectedAreaChanged(this, EventArgs.Empty); + this.SelectedAreaChanged?.Invoke(this, EventArgs.Empty); + } + private void TranslateSelectedArea(int offsetX, int offsetY) + { + if (this.selectedArea.IsEmpty) + { + this.InitializeSelectedArea(); + return; + } + + Rectangle prevSelectedArea = this.SelectedArea; + + int newX = MathF.Clamp(prevSelectedArea.X + offsetX, 0, this.DisplayedTileCount.X - prevSelectedArea.Width); + int newY = MathF.Clamp(prevSelectedArea.Y + offsetY, 0, this.DisplayedTileCount.Y - prevSelectedArea.Height); + + this.SelectedArea = new Rectangle(newX, newY, prevSelectedArea.Width, prevSelectedArea.Height); + this.RaiseSelectedAreaEditingFinished(); + this.Invalidate(); + } + private void ExpandSelectedArea(int diffX, int diffY) + { + if (this.selectedArea.IsEmpty) + { + this.InitializeSelectedArea(); + return; + } + + Rectangle prevSelectedArea = this.SelectedArea; + + int newX = MathF.Max(prevSelectedArea.X + MathF.Min(diffX, 0), 0); + int newY = MathF.Max(prevSelectedArea.Y + MathF.Min(diffY, 0), 0); + + int newWidth = prevSelectedArea.Width + MathF.Max(diffX, 0); + newWidth = MathF.Min(newWidth, this.DisplayedTileCount.X - prevSelectedArea.X); + newWidth += prevSelectedArea.X - newX; + + int newHeight = prevSelectedArea.Height + MathF.Max(diffY, 0); + newHeight = MathF.Min(newHeight, this.DisplayedTileCount.Y - prevSelectedArea.Y); + newHeight += prevSelectedArea.Y - newY; + + this.SelectedArea = new Rectangle(newX, newY, newWidth, newHeight); + this.RaiseSelectedAreaEditingFinished(); + this.Invalidate(); + } + private void ShrinkSelectedArea(int diffX, int diffY) + { + if (this.selectedArea.IsEmpty) + { + this.InitializeSelectedArea(); + return; + } + + Rectangle prevSelectedArea = this.SelectedArea; + + int newX = MathF.Min(prevSelectedArea.X + MathF.Max(diffX, 0), + prevSelectedArea.X + prevSelectedArea.Width - 1); + int newY = MathF.Min(prevSelectedArea.Y + MathF.Max(diffY, 0), + prevSelectedArea.Y + prevSelectedArea.Height - 1); + + int newWidth = MathF.Max(prevSelectedArea.Width + MathF.Min(diffX, 0), 1); + newWidth -= newX - prevSelectedArea.X; + + int newHeight = MathF.Max(prevSelectedArea.Height + MathF.Min(diffY, 0), 1); + newHeight -= newY - prevSelectedArea.Y; + + this.SelectedArea = new Rectangle(newX, newY, newWidth, newHeight); + this.RaiseSelectedAreaEditingFinished(); + this.Invalidate(); } } } \ No newline at end of file diff --git a/Source/Plugins/Tilemaps/Editor/Modules/TilemapToolSourcePalette.cs b/Source/Plugins/Tilemaps/Editor/Modules/TilemapToolSourcePalette.cs index b882d7bbd..840e71830 100644 --- a/Source/Plugins/Tilemaps/Editor/Modules/TilemapToolSourcePalette.cs +++ b/Source/Plugins/Tilemaps/Editor/Modules/TilemapToolSourcePalette.cs @@ -22,6 +22,7 @@ public partial class TilemapToolSourcePalette : DockContent private bool globalEventsSubscribed = false; private TilesetView.TileIndexDrawMode tileIndexDrawMode = TilesetView.TileIndexDrawMode.Never; + public event EventHandler SelectedAreaChanged; private ContentRef SelectedTileset { @@ -42,6 +43,18 @@ public TilemapToolSourcePalette() this.InitializeComponent(); this.mainToolStrip.Renderer = new Duality.Editor.Controls.ToolStrip.DualitorToolStripProfessionalRenderer(); this.ApplyTileIndexDrawMode(); + + this.tilesetView.SelectedAreaChanged += this.SourcePaletteTilesetView_SelectedAreaChanged; + } + + public void RaiseKeyDownEvent(KeyEventArgs e) + { + this.tilesetView.RaiseKeyDownEvent(e); + } + + public void RaiseKeyUpEvent(KeyEventArgs e) + { + this.tilesetView.RaiseKeyUpEvent(e); } internal void SaveUserData(XElement node) @@ -139,7 +152,12 @@ protected override void OnClosed(EventArgs e) base.OnClosed(e); this.OnBecameInvisible(); } - + + private void SourcePaletteTilesetView_SelectedAreaChanged(object sender, EventArgs e) + { + this.SelectedAreaChanged?.Invoke(this, e); + } + private void OnBecameVisible() { if (!this.globalEventsSubscribed)