Improve Win2D editor hover feedback
This commit is contained in:
@@ -25,6 +25,7 @@ This repository follows the local `.editorconfig` and the style visible in the c
|
|||||||
## Braces And Blocks
|
## Braces And Blocks
|
||||||
|
|
||||||
- Use braces for multi-line bodies.
|
- Use braces for multi-line bodies.
|
||||||
|
- If nesting a for-loop under another for-loop, always include curly braces in the parent for-loop.
|
||||||
- Omit braces for simple single-line embedded statements when readability stays clear.
|
- Omit braces for simple single-line embedded statements when readability stays clear.
|
||||||
- Nested control flow with multi-line bodies should use braces at every multi-line level.
|
- Nested control flow with multi-line bodies should use braces at every multi-line level.
|
||||||
- Keep opening braces on the next line for types, methods, properties, accessors, and control blocks.
|
- Keep opening braces on the next line for types, methods, properties, accessors, and control blocks.
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
<Grid Grid.Row="1" ColumnSpacing="0">
|
<Grid Grid.Row="1" ColumnSpacing="0">
|
||||||
<Grid.ColumnDefinitions>
|
<Grid.ColumnDefinitions>
|
||||||
<ColumnDefinition Width="220" />
|
<ColumnDefinition Width="260" />
|
||||||
<ColumnDefinition Width="*" />
|
<ColumnDefinition Width="*" />
|
||||||
<ColumnDefinition Width="300" />
|
<ColumnDefinition Width="300" />
|
||||||
</Grid.ColumnDefinitions>
|
</Grid.ColumnDefinitions>
|
||||||
@@ -30,19 +30,18 @@
|
|||||||
<StackPanel Padding="12" Spacing="10">
|
<StackPanel Padding="12" Spacing="10">
|
||||||
<TextBlock Text="Tools" FontSize="18" FontWeight="SemiBold" Foreground="#F4F1E8" />
|
<TextBlock Text="Tools" FontSize="18" FontWeight="SemiBold" Foreground="#F4F1E8" />
|
||||||
<ItemsControl x:Name="ToolPicker">
|
<ItemsControl x:Name="ToolPicker">
|
||||||
|
<ItemsControl.ItemsPanel>
|
||||||
|
<ItemsPanelTemplate>
|
||||||
|
<ItemsWrapGrid Orientation="Horizontal" MaximumRowsOrColumns="2" />
|
||||||
|
</ItemsPanelTemplate>
|
||||||
|
</ItemsControl.ItemsPanel>
|
||||||
<ItemsControl.ItemTemplate>
|
<ItemsControl.ItemTemplate>
|
||||||
<DataTemplate>
|
<DataTemplate>
|
||||||
<RadioButton GroupName="EditorTools" IsChecked="{Binding IsSelected, Mode=TwoWay}"
|
<ToggleButton IsChecked="{Binding IsSelected, Mode=TwoWay}"
|
||||||
Checked="ToolRadio_Checked">
|
Checked="ToolToggle_Checked" ToolTipService.ToolTip="{Binding Label}"
|
||||||
<Grid ColumnSpacing="8">
|
Padding="5" Margin="0,0,8,8">
|
||||||
<Grid.ColumnDefinitions>
|
<Image Width="96" Height="96" Source="{Binding Icon}" Stretch="Uniform" />
|
||||||
<ColumnDefinition Width="28" />
|
</ToggleButton>
|
||||||
<ColumnDefinition Width="*" />
|
|
||||||
</Grid.ColumnDefinitions>
|
|
||||||
<Image Width="24" Height="24" Source="{Binding Icon}" />
|
|
||||||
<TextBlock Grid.Column="1" Text="{Binding Label}" />
|
|
||||||
</Grid>
|
|
||||||
</RadioButton>
|
|
||||||
</DataTemplate>
|
</DataTemplate>
|
||||||
</ItemsControl.ItemTemplate>
|
</ItemsControl.ItemTemplate>
|
||||||
</ItemsControl>
|
</ItemsControl>
|
||||||
@@ -61,6 +60,7 @@
|
|||||||
PointerPressed="LevelCanvas_PointerPressed"
|
PointerPressed="LevelCanvas_PointerPressed"
|
||||||
PointerMoved="LevelCanvas_PointerMoved"
|
PointerMoved="LevelCanvas_PointerMoved"
|
||||||
PointerReleased="LevelCanvas_PointerReleased"
|
PointerReleased="LevelCanvas_PointerReleased"
|
||||||
|
PointerExited="LevelCanvas_PointerExited"
|
||||||
PointerWheelChanged="LevelCanvas_PointerWheelChanged" />
|
PointerWheelChanged="LevelCanvas_PointerWheelChanged" />
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using Microsoft.Graphics.Canvas.UI;
|
using Microsoft.Graphics.Canvas.UI;
|
||||||
using Microsoft.Graphics.Canvas.UI.Xaml;
|
using Microsoft.Graphics.Canvas.UI.Xaml;
|
||||||
using Microsoft.UI;
|
using Microsoft.UI;
|
||||||
using Microsoft.UI.Input;
|
|
||||||
using Microsoft.UI.Xaml;
|
using Microsoft.UI.Xaml;
|
||||||
using Microsoft.UI.Xaml.Controls;
|
using Microsoft.UI.Xaml.Controls;
|
||||||
using Microsoft.UI.Xaml.Input;
|
using Microsoft.UI.Xaml.Input;
|
||||||
@@ -12,9 +11,7 @@ using System.Globalization;
|
|||||||
using Windows.Foundation;
|
using Windows.Foundation;
|
||||||
using Windows.Storage;
|
using Windows.Storage;
|
||||||
using Windows.Storage.Pickers;
|
using Windows.Storage.Pickers;
|
||||||
using Windows.System;
|
|
||||||
using Windows.UI;
|
using Windows.UI;
|
||||||
using Windows.UI.Core;
|
|
||||||
using Windows.UI.Popups;
|
using Windows.UI.Popups;
|
||||||
using WinRT.Interop;
|
using WinRT.Interop;
|
||||||
|
|
||||||
@@ -86,7 +83,7 @@ public sealed partial class MainWindow
|
|||||||
return await CanvasBitmap.LoadAsync(sender, path);
|
return await CanvasBitmap.LoadAsync(sender, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToolRadio_Checked(object sender, RoutedEventArgs e)
|
private void ToolToggle_Checked(object sender, RoutedEventArgs e)
|
||||||
{
|
{
|
||||||
if ((sender as FrameworkElement)?.DataContext is EditorToolViewModel tool)
|
if ((sender as FrameworkElement)?.DataContext is EditorToolViewModel tool)
|
||||||
{
|
{
|
||||||
@@ -176,15 +173,6 @@ public sealed partial class MainWindow
|
|||||||
private void LevelCanvas_PointerPressed(object sender, PointerRoutedEventArgs e)
|
private void LevelCanvas_PointerPressed(object sender, PointerRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var point = e.GetCurrentPoint(LevelCanvas);
|
var point = e.GetCurrentPoint(LevelCanvas);
|
||||||
if (point.Properties.IsLeftButtonPressed && IsShiftDown())
|
|
||||||
{
|
|
||||||
m_Panning = true;
|
|
||||||
m_LastPanPoint = point.Position;
|
|
||||||
_ = LevelCanvas.CapturePointer(e.Pointer);
|
|
||||||
e.Handled = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (point.Properties.IsRightButtonPressed)
|
if (point.Properties.IsRightButtonPressed)
|
||||||
{
|
{
|
||||||
RemovePropAt(point.Position);
|
RemovePropAt(point.Position);
|
||||||
@@ -195,8 +183,10 @@ public sealed partial class MainWindow
|
|||||||
if (point.Properties.IsLeftButtonPressed)
|
if (point.Properties.IsLeftButtonPressed)
|
||||||
{
|
{
|
||||||
_ = LevelCanvas.CapturePointer(e.Pointer);
|
_ = LevelCanvas.CapturePointer(e.Pointer);
|
||||||
SelectOrPaintAt(point.Position);
|
m_LeftPointerDown = true;
|
||||||
m_Painting = m_SelectedTool != EEditorTool.Cursor;
|
m_LeftPointerDownPoint = point.Position;
|
||||||
|
m_LastPanPoint = point.Position;
|
||||||
|
m_DragExceededClickThreshold = false;
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -204,11 +194,17 @@ public sealed partial class MainWindow
|
|||||||
private void LevelCanvas_PointerMoved(object sender, PointerRoutedEventArgs e)
|
private void LevelCanvas_PointerMoved(object sender, PointerRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var point = e.GetCurrentPoint(LevelCanvas);
|
var point = e.GetCurrentPoint(LevelCanvas);
|
||||||
if (m_Panning)
|
if (m_LeftPointerDown)
|
||||||
{
|
{
|
||||||
var deltaX = point.Position.X - m_LastPanPoint.X;
|
var deltaX = point.Position.X - m_LastPanPoint.X;
|
||||||
var deltaY = point.Position.Y - m_LastPanPoint.Y;
|
var deltaY = point.Position.Y - m_LastPanPoint.Y;
|
||||||
m_LastPanPoint = point.Position;
|
m_LastPanPoint = point.Position;
|
||||||
|
|
||||||
|
var totalDeltaX = point.Position.X - m_LeftPointerDownPoint.X;
|
||||||
|
var totalDeltaY = point.Position.Y - m_LeftPointerDownPoint.Y;
|
||||||
|
if (Math.Sqrt((totalDeltaX * totalDeltaX) + (totalDeltaY * totalDeltaY)) > c_ClickPixelThreshold)
|
||||||
|
m_DragExceededClickThreshold = true;
|
||||||
|
|
||||||
m_PanX += deltaX;
|
m_PanX += deltaX;
|
||||||
m_PanY += deltaY;
|
m_PanY += deltaY;
|
||||||
ClampPan();
|
ClampPan();
|
||||||
@@ -217,21 +213,26 @@ public sealed partial class MainWindow
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_Painting)
|
SetHoveredCell(point.Position);
|
||||||
{
|
|
||||||
PaintAt(point.Position);
|
|
||||||
e.Handled = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LevelCanvas_PointerReleased(object sender, PointerRoutedEventArgs e)
|
private void LevelCanvas_PointerReleased(object sender, PointerRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
m_Painting = false;
|
var point = e.GetCurrentPoint(LevelCanvas);
|
||||||
m_Panning = false;
|
if (m_LeftPointerDown && !m_DragExceededClickThreshold)
|
||||||
|
SelectOrPaintAt(point.Position);
|
||||||
|
|
||||||
|
m_LeftPointerDown = false;
|
||||||
|
m_DragExceededClickThreshold = false;
|
||||||
LevelCanvas.ReleasePointerCapture(e.Pointer);
|
LevelCanvas.ReleasePointerCapture(e.Pointer);
|
||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void LevelCanvas_PointerExited(object sender, PointerRoutedEventArgs e)
|
||||||
|
{
|
||||||
|
ClearHoveredCell();
|
||||||
|
}
|
||||||
|
|
||||||
private void LevelCanvas_PointerWheelChanged(object sender, PointerRoutedEventArgs e)
|
private void LevelCanvas_PointerWheelChanged(object sender, PointerRoutedEventArgs e)
|
||||||
{
|
{
|
||||||
var point = e.GetCurrentPoint(LevelCanvas);
|
var point = e.GetCurrentPoint(LevelCanvas);
|
||||||
@@ -243,11 +244,6 @@ public sealed partial class MainWindow
|
|||||||
e.Handled = true;
|
e.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static bool IsShiftDown()
|
|
||||||
{
|
|
||||||
return InputKeyboardSource.GetKeyStateForCurrentThread(VirtualKey.Shift).HasFlag(CoreVirtualKeyStates.Down);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ZoomAt(Point point, double zoomFactor)
|
private void ZoomAt(Point point, double zoomFactor)
|
||||||
{
|
{
|
||||||
var oldLayout = GetLayout();
|
var oldLayout = GetLayout();
|
||||||
@@ -314,7 +310,7 @@ public sealed partial class MainWindow
|
|||||||
drawing.Clear(ColorHelper.FromArgb(255, 16, 18, 21));
|
drawing.Clear(ColorHelper.FromArgb(255, 16, 18, 21));
|
||||||
DrawTerrain(drawing, layout);
|
DrawTerrain(drawing, layout);
|
||||||
DrawCellOverlays(drawing, layout);
|
DrawCellOverlays(drawing, layout);
|
||||||
DrawGrid(drawing, layout);
|
//DrawGrid(drawing, layout);
|
||||||
DrawRobot(drawing, layout);
|
DrawRobot(drawing, layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,10 +346,13 @@ public sealed partial class MainWindow
|
|||||||
if (cell.Hazards.Fire)
|
if (cell.Hazards.Fire)
|
||||||
DrawImage(drawing, m_FireSprite, Inset(rect, 0.08));
|
DrawImage(drawing, m_FireSprite, Inset(rect, 0.08));
|
||||||
|
|
||||||
|
DrawCellProp(drawing, cell, rect);
|
||||||
|
|
||||||
|
if (m_HoveredCell == position)
|
||||||
|
drawing.FillRectangle(rect, ColorHelper.FromArgb(72, 255, 255, 255));
|
||||||
|
|
||||||
if (m_SelectedCell == position)
|
if (m_SelectedCell == position)
|
||||||
drawing.DrawRectangle(rect, Colors.White, 3);
|
drawing.DrawRectangle(rect, Colors.White, 3);
|
||||||
|
|
||||||
DrawCellProp(drawing, cell, rect);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -364,7 +363,7 @@ public sealed partial class MainWindow
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
var sourceRect = PipeTileSourceRect(GetPipeConnectionMask(position, cell.Pipe));
|
var sourceRect = PipeTileSourceRect(GetPipeConnectionMask(position, cell.Pipe));
|
||||||
drawing.DrawImage(tilemap, rect, sourceRect);
|
drawing.DrawImage(tilemap, rect, sourceRect, 1.0f, CanvasImageInterpolation.HighQualityCubic);
|
||||||
}
|
}
|
||||||
|
|
||||||
private int GetPipeConnectionMask(GridPosition position, EPipeMedium medium)
|
private int GetPipeConnectionMask(GridPosition position, EPipeMedium medium)
|
||||||
@@ -422,7 +421,7 @@ public sealed partial class MainWindow
|
|||||||
private static void DrawImage(CanvasDrawingSession drawing, CanvasBitmap? image, Rect rect, float opacity = 1)
|
private static void DrawImage(CanvasDrawingSession drawing, CanvasBitmap? image, Rect rect, float opacity = 1)
|
||||||
{
|
{
|
||||||
if (image is not null)
|
if (image is not null)
|
||||||
drawing.DrawImage(image, rect, image.Bounds, opacity);
|
drawing.DrawImage(image, rect, image.Bounds, opacity, CanvasImageInterpolation.HighQualityCubic);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Rect Inset(Rect rect, double fraction)
|
private static Rect Inset(Rect rect, double fraction)
|
||||||
@@ -438,7 +437,7 @@ public sealed partial class MainWindow
|
|||||||
|
|
||||||
var wallMask = c_AllCorners ^ floorMask;
|
var wallMask = c_AllCorners ^ floorMask;
|
||||||
var sourceRect = TilemapSourceRect(wallMask);
|
var sourceRect = TilemapSourceRect(wallMask);
|
||||||
drawing.DrawImage(m_TerrainTilemap, rect, sourceRect);
|
drawing.DrawImage(m_TerrainTilemap, rect, sourceRect, 1.0f, CanvasImageInterpolation.HighQualityCubic);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Rect TilemapSourceRect(int wallMask)
|
private static Rect TilemapSourceRect(int wallMask)
|
||||||
@@ -497,7 +496,7 @@ public sealed partial class MainWindow
|
|||||||
private void DrawCellProp(CanvasDrawingSession drawing, CellState cell, Rect rect)
|
private void DrawCellProp(CanvasDrawingSession drawing, CellState cell, Rect rect)
|
||||||
{
|
{
|
||||||
if (m_PropSprites.TryGetValue(cell.Prop, out var sprite))
|
if (m_PropSprites.TryGetValue(cell.Prop, out var sprite))
|
||||||
drawing.DrawImage(sprite, rect, sprite.Bounds);
|
drawing.DrawImage(sprite, rect, sprite.Bounds, 1.0f, CanvasImageInterpolation.HighQualityCubic);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DrawGrid(CanvasDrawingSession drawing, CanvasLayout layout)
|
private void DrawGrid(CanvasDrawingSession drawing, CanvasLayout layout)
|
||||||
@@ -530,6 +529,25 @@ public sealed partial class MainWindow
|
|||||||
return m_Level.InBounds(position);
|
return m_Level.InBounds(position);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void SetHoveredCell(Point point)
|
||||||
|
{
|
||||||
|
var hoveredCell = TryGetGridPosition(point, out var position) ? position : (GridPosition?)null;
|
||||||
|
if (m_HoveredCell == hoveredCell)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_HoveredCell = hoveredCell;
|
||||||
|
LevelCanvas.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearHoveredCell()
|
||||||
|
{
|
||||||
|
if (m_HoveredCell is null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_HoveredCell = null;
|
||||||
|
LevelCanvas.Invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
private CanvasLayout GetLayout()
|
private CanvasLayout GetLayout()
|
||||||
{
|
{
|
||||||
ClampPan();
|
ClampPan();
|
||||||
@@ -715,6 +733,7 @@ public sealed partial class MainWindow
|
|||||||
private const double c_MinZoom = 0.5;
|
private const double c_MinZoom = 0.5;
|
||||||
private const double c_MaxZoom = 4;
|
private const double c_MaxZoom = 4;
|
||||||
private const double c_ZoomStep = 1.15;
|
private const double c_ZoomStep = 1.15;
|
||||||
|
private const double c_ClickPixelThreshold = 5;
|
||||||
|
|
||||||
private readonly SimulationEngine m_Simulation = new();
|
private readonly SimulationEngine m_Simulation = new();
|
||||||
private readonly Dictionary<ECellProp, CanvasBitmap> m_PropSprites = [];
|
private readonly Dictionary<ECellProp, CanvasBitmap> m_PropSprites = [];
|
||||||
@@ -722,9 +741,11 @@ public sealed partial class MainWindow
|
|||||||
private StorageFile? m_CurrentFile;
|
private StorageFile? m_CurrentFile;
|
||||||
private LevelState m_Level;
|
private LevelState m_Level;
|
||||||
private IReadOnlyList<EditorToolViewModel> m_EditorTools = [];
|
private IReadOnlyList<EditorToolViewModel> m_EditorTools = [];
|
||||||
private bool m_Painting;
|
private bool m_LeftPointerDown;
|
||||||
private bool m_Panning;
|
private bool m_DragExceededClickThreshold;
|
||||||
|
private Point m_LeftPointerDownPoint;
|
||||||
private Point m_LastPanPoint;
|
private Point m_LastPanPoint;
|
||||||
|
private GridPosition? m_HoveredCell;
|
||||||
private GridPosition? m_SelectedCell;
|
private GridPosition? m_SelectedCell;
|
||||||
private EEditorTool m_SelectedTool = EEditorTool.Cursor;
|
private EEditorTool m_SelectedTool = EEditorTool.Cursor;
|
||||||
private double m_Zoom = 1;
|
private double m_Zoom = 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user