Skip to content

Commit 828e2ea

Browse files
Merge pull request #18 from RuntimeRascal/feat/support-line-draw
Add Line Drawing Tool
2 parents 13787ad + 0aa9b68 commit 828e2ea

19 files changed

Lines changed: 1330 additions & 170 deletions

CHANGELOG.md

Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
# Changelog
2+
3+
All notable changes to GhostDraw will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7+
8+
## v1.0.5
9+
10+
### Added
11+
- **Line Tool** - Draw straight lines by clicking two points
12+
- Press `L` to activate Line tool
13+
- First click sets start point, second click finalizes the line
14+
- Real-time preview follows cursor
15+
- Line thickness and color adjust dynamically with mouse wheel and right-click
16+
- **Explicit Tool Selection** - Direct keyboard shortcuts for tool switching
17+
- Press `P` to select Pen tool (freehand drawing)
18+
- Press `L` to select Line tool (straight lines)
19+
- **Keyboard Shortcuts Help** - F1 help popup showing all available shortcuts
20+
- Press `F1` while in drawing mode to display help overlay
21+
- Shows current activation hotkey combination dynamically
22+
- Displays all drawing tools, actions, and system shortcuts
23+
- Auto-fades after 4 seconds with smooth animations
24+
- **Settings Architecture Improvements**
25+
- Abstracted settings persistence with `ISettingsStore` interface
26+
- `FileSettingsStore` for production file-based persistence
27+
- `InMemorySettingsStore` for isolated unit testing
28+
- Eliminated file system dependencies from unit tests
29+
30+
### Changed
31+
- Tool selection now uses explicit methods instead of toggle-only behavior
32+
- Test suite refactored to use in-memory storage, eliminating test pollution
33+
- Settings service now uses dependency injection for storage layer
34+
35+
### Fixed
36+
- Line tool cursor hotspot now correctly positioned at line start point (left circle)
37+
- Test isolation issues resolved - no more shared file system state
38+
39+
## v1.0.0-v1.0.4 - Initial Releases
40+
41+
### Added
42+
- **Drawing Mode Activation**
43+
- Configurable hotkey combination (default: Ctrl+Alt+D)
44+
- Toggle or hold mode options
45+
- **Pen Tool** - Freehand drawing with mouse
46+
- Click and drag to draw smooth polylines
47+
- Adjustable brush thickness (1-20 pixels)
48+
- 10-color palette with cycling
49+
- **Brush Customization**
50+
- Right-click to cycle through color palette
51+
- Mouse wheel to adjust brush thickness
52+
- Real-time thickness indicator overlay
53+
- **Canvas Management**
54+
- Press `R` to clear canvas with visual feedback
55+
- Press `ESC` for emergency exit from drawing mode
56+
- Canvas spans all monitors in multi-monitor setups
57+
- **System Tray Integration**
58+
- Always-running background application
59+
- Context menu for quick access to settings
60+
- Log level adjustment from tray
61+
- **Settings Window**
62+
- Cyberpunk-themed UI
63+
- Drawing settings (brush thickness, color palette)
64+
- Hotkey configuration with visual recorder
65+
- Mode settings (toggle vs hold)
66+
- Logging level configuration
67+
- **Robust Error Handling**
68+
- Global exception handler prevents system lockout
69+
- Comprehensive logging with Serilog
70+
- Safe hook cleanup on exit
71+
- **Custom Cursors**
72+
- Colored pencil cursor shows active brush color
73+
- Line tool cursor with dual circles and connecting line
74+
75+
### Technical Features
76+
- WPF transparent overlay across all screens
77+
- Global Windows keyboard/mouse hooks
78+
- Dependency injection with Microsoft.Extensions.DependencyInjection
79+
- Structured logging with Serilog
80+
- Settings persistence in LocalApplicationData
81+
- .NET 8 target framework
82+
83+
---
84+
85+
## How to Update This Changelog
86+
87+
When adding new features or fixes:
88+
89+
1. Add entries under the `[Unreleased]` section
90+
2. Use these categories:
91+
- `Added` - New features
92+
- `Changed` - Changes to existing functionality
93+
- `Deprecated` - Soon-to-be removed features
94+
- `Removed` - Removed features
95+
- `Fixed` - Bug fixes
96+
- `Security` - Security fixes
97+
98+
3. When releasing a new version:
99+
- Change `[Unreleased]` to `[X.Y.Z] - YYYY-MM-DD`
100+
- Create a new `[Unreleased]` section at the top
101+
- Update version numbers according to [Semantic Versioning](https://semver.org/):
102+
- MAJOR version for incompatible API changes
103+
- MINOR version for backwards-compatible functionality additions
104+
- PATCH version for backwards-compatible bug fixes
105+
106+
### Example Entry Format
107+
108+
```markdown
109+
## [Unreleased]
110+
111+
### Added
112+
- **Feature Name** - Brief description
113+
- Detail about feature
114+
- Another detail
115+
116+
### Fixed
117+
- Issue description and what was fixed
118+
```

Installer/GhostDraw.Installer.wixproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<Project Sdk="WixToolset.Sdk/4.0.5">
22
<PropertyGroup>
3-
<Version Condition="'$(Version)' == ''">1.0.4</Version>
3+
<Version Condition="'$(Version)' == ''">1.0.5</Version>
44
<OutputName>GhostDrawSetup-$(Version)</OutputName>
55
<OutputType>Package</OutputType>
66
<Platform>x64</Platform>

Src/GhostDraw/App.xaml.cs

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,9 @@ protected override void OnStartup(StartupEventArgs e)
7474
_keyboardHook.HotkeyReleased += OnHotkeyReleased;
7575
_keyboardHook.EscapePressed += OnEscapePressed;
7676
_keyboardHook.ClearCanvasPressed += OnClearCanvasPressed;
77+
_keyboardHook.PenToolPressed += OnPenToolPressed;
78+
_keyboardHook.LineToolPressed += OnLineToolPressed;
79+
_keyboardHook.HelpPressed += OnHelpPressed;
7780
_keyboardHook.Start();
7881

7982
// Setup system tray icon
@@ -251,6 +254,57 @@ private void OnClearCanvasPressed(object? sender, EventArgs e)
251254
}
252255
}
253256

257+
private void OnPenToolPressed(object? sender, EventArgs e)
258+
{
259+
try
260+
{
261+
// Only switch to pen tool if drawing mode is active
262+
if (_drawingManager?.IsDrawingMode == true)
263+
{
264+
_logger?.LogInformation("P pressed - selecting pen tool");
265+
_drawingManager?.SetPenTool();
266+
}
267+
}
268+
catch (Exception ex)
269+
{
270+
_exceptionHandler?.HandleException(ex, "Pen tool handler");
271+
}
272+
}
273+
274+
private void OnLineToolPressed(object? sender, EventArgs e)
275+
{
276+
try
277+
{
278+
// Only switch to line tool if drawing mode is active
279+
if (_drawingManager?.IsDrawingMode == true)
280+
{
281+
_logger?.LogInformation("L pressed - selecting line tool");
282+
_drawingManager?.SetLineTool();
283+
}
284+
}
285+
catch (Exception ex)
286+
{
287+
_exceptionHandler?.HandleException(ex, "Line tool handler");
288+
}
289+
}
290+
291+
private void OnHelpPressed(object? sender, EventArgs e)
292+
{
293+
try
294+
{
295+
// Show help overlay if drawing mode is active
296+
if (_drawingManager?.IsDrawingMode == true)
297+
{
298+
_logger?.LogInformation("F1 pressed - showing help");
299+
_drawingManager?.ShowHelp();
300+
}
301+
}
302+
catch (Exception ex)
303+
{
304+
_exceptionHandler?.HandleException(ex, "Help pressed handler");
305+
}
306+
}
307+
254308
protected override void OnExit(ExitEventArgs e)
255309
{
256310
_logger?.LogInformation("Application exiting");

Src/GhostDraw/Core/AppSettings.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,12 @@ public class AppSettings
3131
[JsonPropertyName("maxBrushThickness")]
3232
public double MaxBrushThickness { get; set; } = 20.0;
3333

34+
/// <summary>
35+
/// The currently active drawing tool
36+
/// </summary>
37+
[JsonPropertyName("activeTool")]
38+
public DrawTool ActiveTool { get; set; } = DrawTool.Pen;
39+
3440
/// <summary>
3541
/// Virtual key codes for the hotkey combination
3642
/// </summary>
@@ -86,6 +92,7 @@ public AppSettings Clone()
8692
BrushThickness = BrushThickness,
8793
MinBrushThickness = MinBrushThickness,
8894
MaxBrushThickness = MaxBrushThickness,
95+
ActiveTool = ActiveTool,
8996
HotkeyVirtualKeys = new List<int>(HotkeyVirtualKeys),
9097
LockDrawingMode = LockDrawingMode,
9198
LogLevel = LogLevel,

Src/GhostDraw/Core/DrawTool.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace GhostDraw.Core;
4+
5+
/// <summary>
6+
/// Available drawing tools
7+
/// </summary>
8+
[JsonConverter(typeof(JsonStringEnumConverter))]
9+
public enum DrawTool
10+
{
11+
/// <summary>
12+
/// Freehand drawing tool (default)
13+
/// </summary>
14+
Pen,
15+
16+
/// <summary>
17+
/// Straight line tool - click two points to draw a line
18+
/// </summary>
19+
Line
20+
}

Src/GhostDraw/Core/GlobalKeyboardHook.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@ public class GlobalKeyboardHook : IDisposable
1313
// Only keep VK_ESCAPE constant (emergency exit)
1414
private const int VK_ESCAPE = 0x1B; // 27
1515
private const int VK_R = 0x52; // 82 - 'R' key for clear canvas
16+
private const int VK_L = 0x4C; // 76 - 'L' key for line tool
17+
private const int VK_P = 0x50; // 80 - 'P' key for pen tool
18+
private const int VK_F1 = 0x70; // 112 - 'F1' key for help
1619

1720
private readonly ILogger<GlobalKeyboardHook> _logger;
1821
private readonly LowLevelKeyboardProc _proc;
@@ -25,6 +28,9 @@ public class GlobalKeyboardHook : IDisposable
2528
public event EventHandler? HotkeyReleased;
2629
public event EventHandler? EscapePressed;
2730
public event EventHandler? ClearCanvasPressed;
31+
public event EventHandler? PenToolPressed;
32+
public event EventHandler? LineToolPressed;
33+
public event EventHandler? HelpPressed;
2834

2935
// NEW: Raw key events for recorder
3036
public event EventHandler<KeyEventArgs>? KeyPressed;
@@ -190,6 +196,27 @@ private nint HookCallback(int nCode, nint wParam, nint lParam)
190196
ClearCanvasPressed?.Invoke(this, EventArgs.Empty);
191197
}
192198

199+
// Check for L key press (line tool)
200+
if (vkCode == VK_L && isKeyDown)
201+
{
202+
_logger.LogDebug("L key pressed - line tool request");
203+
LineToolPressed?.Invoke(this, EventArgs.Empty);
204+
}
205+
206+
// Check for P key press (pen tool)
207+
if (vkCode == VK_P && isKeyDown)
208+
{
209+
_logger.LogDebug("P key pressed - pen tool request");
210+
PenToolPressed?.Invoke(this, EventArgs.Empty);
211+
}
212+
213+
// Check for F1 key press (help)
214+
if (vkCode == VK_F1 && isKeyDown)
215+
{
216+
_logger.LogDebug("F1 key pressed - help request");
217+
HelpPressed?.Invoke(this, EventArgs.Empty);
218+
}
219+
193220
// Track hotkey state
194221
if (_hotkeyVKs.Contains(vkCode))
195222
{

Src/GhostDraw/Core/ServiceConfiguration.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,9 @@ public static ServiceProvider ConfigureServices()
5151
builder.AddSerilog(dispose: true);
5252
});
5353

54+
// Register settings store (file-based for production)
55+
services.AddSingleton<ISettingsStore, FileSettingsStore>();
56+
5457
// Register application services (order matters for dependencies)
5558
services.AddSingleton<AppSettingsService>();
5659
services.AddSingleton<CursorHelper>();

Src/GhostDraw/GhostDraw.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<UseWPF>true</UseWPF>
99
<UseWindowsForms>true</UseWindowsForms>
1010
<ApplicationIcon>Assets\favicon.ico</ApplicationIcon>
11-
<Version>1.0.4</Version>
11+
<Version>1.0.5</Version>
1212
</PropertyGroup>
1313

1414
<ItemGroup>

0 commit comments

Comments
 (0)