diff --git a/Directory.Build.props b/Directory.Build.props
index 1ab2674..1199a98 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -5,7 +5,7 @@
scripts/build-installer.ps1 both read it from here, so releasing is a reviewed change
to this line rather than an edit in a pipeline variable group.
-->
- 1.1.4
+ 1.1.5latestenableenable
diff --git a/README.md b/README.md
index b51d7bf..687f405 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,7 @@ How the project is developed and shipped is documented separately:
- A normal cursor for physical mouse input, and a pen-hover indicator that shows what a tap would do: the laser with its halo and speed trail, a dashed square around what the eraser would clear, and a high-contrast dot for everything else. All of them disappear on contact
- Touch panning and two-finger pinch zoom
- Optional finger drawing (default when no pen is detected): one finger uses the current tool, two fingers still pan and pinch-zoom, and Eraser and Pan appear on the toolbar
+- A notice at startup when Windows reports neither a pen tablet nor a touchscreen: the application still opens, but there is nothing to draw with, and [discussion 78](https://github.com/sql-bi/SQLBI-Whiteboard/discussions/78) collects votes for mouse-only drawing. Dismissable from the notice itself or from Preferences, since the tablet list Windows reports can miss a pen that has never been in range
- Basic palm rejection: touch navigation is suspended when the pen makes contact
- Mouse-wheel zoom and middle-button or temporary Space-key panning
- Whole-stroke erasing
@@ -202,7 +203,7 @@ Use **Copy settings** after finding a useful combination so the exact values can
| Delete | Delete the selected container and its linked strokes |
| Alt+L | Laser pointer |
| File / Edit / View / Help | Tab strip. Click a tab for a one-row command strip over the canvas. Click the canvas to hide it |
-| Help > Preferences | Searchable settings: startup monitor, full screen, finger drawing, pen button, snippet format order, laser trail, toolbar, update checks |
+| Help > Preferences | Searchable settings: startup monitor, full screen, no-pen warning, finger drawing, pen button, snippet format order, laser trail, toolbar, update checks |
| View > Bring to front / Send to back | Reorder the selected image, text, or LiveView (and its linked strokes) |
| Help > About | Version, channel, license, the product site, and a download link when a newer release is known |
| View > LiveView | Capture, freeze, disconnect, or reconnect a window or display |
diff --git a/TODO.md b/TODO.md
index 2a54152..c40869d 100644
--- a/TODO.md
+++ b/TODO.md
@@ -16,7 +16,7 @@ The delivery chain works end to end: a merge to `main` builds, signs, and publis
pre-release to GitHub Releases, and one approval promotes that same build to a release.
reads its download links from the release manifest
deployed beside it and needs no edit per release. The current product version is `VersionPrefix` in `Directory.Build.props`
-(1.1.4). Identity version for the Store package is `VersionPrefix.0` (`1.1.4.0`).
+(1.1.5). Identity version for the Store package is `VersionPrefix.0` (`1.1.5.0`).
Declaring that number is decision 20 in [docs/decisions.md](docs/decisions.md). What 1.0
was waiting on shipped during 0.9.x: Preferences, `.wimport`, Explorer and VS Code
diff --git a/site/faq.html b/site/faq.html
index 2dd56dd..2e08811 100644
--- a/site/faq.html
+++ b/site/faq.html
@@ -57,7 +57,7 @@
Windows 11 or Windows 10?
Built for Windows 11. It runs on Windows 10 version 2004 or later, 64-bit. That is the same floor as Windows Graphics Capture, which LiveView uses.
Is a pen required?
-
For ink, yes, unless Finger drawing is on. A mouse and touch pan, zoom, and move containers. A keyboard runs the rest. The rear eraser and palm rejection still need a pen. New installs default Finger drawing to When no pen is detected, which looks at the digitizer list Windows reports, not whether a pen is in the room. On makes one finger use the current tool; two fingers still pan and pinch-zoom, and Eraser and Pan appear on the toolbar. The shortcut list is grouped by pen, mouse, touch, and keys.
+
For ink, yes, unless Finger drawing is on. On a computer where Windows reports neither a pen tablet nor a touchscreen, a notice at startup says so and points to discussion 78, which collects votes for mouse-only drawing; the application still opens. A mouse and touch pan, zoom, and move containers. A keyboard runs the rest. The rear eraser and palm rejection still need a pen. New installs default Finger drawing to When no pen is detected, which looks at the digitizer list Windows reports, not whether a pen is in the room. On makes one finger use the current tool; two fingers still pan and pinch-zoom, and Eraser and Pan appear on the toolbar. The shortcut list is grouped by pen, mouse, touch, and keys.
Mac, iPad, or a browser?
No. It is a Windows application. Boards are files you can keep next to the material they document; they do not open in a browser.
Help → Preferences is a searchable list: which monitor to open on, start full screen, finger drawing, what the pen's barrel button does, snippet format order, laser trail timing and weight, toolbar placement and layout, and the daily new-version check. Settings about how something looks — the trail weight, the barrel button, where the toolbar sits and how it is laid out — draw their options rather than naming them, so the choice is made by looking. Help → About shows the version. New settings are more rows in Preferences, not a new dialog.
+
Help → Preferences is a searchable list: which monitor to open on, start full screen, whether to warn when there is nothing to draw with, finger drawing, what the pen's barrel button does, snippet format order, laser trail timing and weight, toolbar placement and layout, and the daily new-version check. Settings about how something looks — the trail weight, the barrel button, where the toolbar sits and how it is laid out — draw their options rather than naming them, so the choice is made by looking. Help → About shows the version. New settings are more rows in Preferences, not a new dialog.
diff --git a/src/SQLBI.Whiteboard.Core/Settings/AppSettings.cs b/src/SQLBI.Whiteboard.Core/Settings/AppSettings.cs
index 6d82962..c1f70c0 100644
--- a/src/SQLBI.Whiteboard.Core/Settings/AppSettings.cs
+++ b/src/SQLBI.Whiteboard.Core/Settings/AppSettings.cs
@@ -98,6 +98,15 @@ public sealed class AppSettings
public PenButtonSettings PenButtons { get; set; } = new();
+ ///
+ /// Whether to say at startup that Windows reports nothing to draw with. The
+ /// tablet list this reads is a list of digitizers rather than an answer
+ /// about what is plugged in, so a pen that has never been brought into
+ /// range can be missing from it - which is the other reason this can be
+ /// turned off.
+ ///
+ public bool WarnWhenNoDigitizer { get; set; } = true;
+
public bool CheckForUpdates { get; set; } = true;
public DateTimeOffset? LastUpdateCheckUtc { get; set; }
@@ -111,7 +120,7 @@ public sealed class AppSettings
public static class AppSettingsSerializer
{
- public const int CurrentVersion = 11;
+ public const int CurrentVersion = 12;
private static readonly JsonSerializerOptions JsonOptions = new()
{
diff --git a/src/SQLBI.Whiteboard/MainWindow.xaml.cs b/src/SQLBI.Whiteboard/MainWindow.xaml.cs
index 6abd780..89e2f93 100644
--- a/src/SQLBI.Whiteboard/MainWindow.xaml.cs
+++ b/src/SQLBI.Whiteboard/MainWindow.xaml.cs
@@ -196,6 +196,7 @@ private void MainWindow_SourceInitialized(object? sender, EventArgs e)
private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
{
Loaded -= MainWindow_Loaded;
+ WarnWhenNothingToDrawWith();
_ = CheckForUpdatesAsync();
if (_initialBoardPath is not null)
{
@@ -203,6 +204,26 @@ private async void MainWindow_Loaded(object sender, RoutedEventArgs e)
}
}
+ // A whiteboard with nothing to draw on it is worth saying out loud, rather
+ // than leaving someone to work out why the pen tools do nothing.
+ private void WarnWhenNothingToDrawWith()
+ {
+ if (!_settings.WarnWhenNoDigitizer || NoDigitizerWindow.HasDrawingDevice())
+ {
+ return;
+ }
+
+ var notice = new NoDigitizerWindow { Owner = this };
+ notice.ShowDialog();
+ if (!notice.DoNotShowAgain)
+ {
+ return;
+ }
+
+ _settings.WarnWhenNoDigitizer = false;
+ AppSettingsStore.Save(_settings);
+ }
+
private void BoardViewport_SizeChanged(object sender, SizeChangedEventArgs e)
{
_camera.Resize(e.NewSize.Width, e.NewSize.Height);
diff --git a/src/SQLBI.Whiteboard/NoDigitizerWindow.xaml b/src/SQLBI.Whiteboard/NoDigitizerWindow.xaml
new file mode 100644
index 0000000..85aee60
--- /dev/null
+++ b/src/SQLBI.Whiteboard/NoDigitizerWindow.xaml
@@ -0,0 +1,60 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/SQLBI.Whiteboard/NoDigitizerWindow.xaml.cs b/src/SQLBI.Whiteboard/NoDigitizerWindow.xaml.cs
new file mode 100644
index 0000000..b28e422
--- /dev/null
+++ b/src/SQLBI.Whiteboard/NoDigitizerWindow.xaml.cs
@@ -0,0 +1,63 @@
+using System.Diagnostics;
+using System.Windows;
+using System.Windows.Input;
+using System.Windows.Navigation;
+
+namespace SQLBI.Whiteboard;
+
+///
+/// Shown once at startup when Windows reports neither a pen tablet nor a
+/// touchscreen. The application still opens - everything that does not need ink
+/// works with a mouse and the keyboard - so this says what is missing rather
+/// than refusing to start.
+///
+public partial class NoDigitizerWindow : Window
+{
+ public NoDigitizerWindow() => InitializeComponent();
+
+ ///
+ /// Whether the notice was dismissed for good. Also the way out of a false
+ /// positive, so it is offered on the notice itself rather than only in
+ /// Preferences.
+ ///
+ public bool DoNotShowAgain => DoNotShowAgainBox.IsChecked == true;
+
+ ///
+ /// Whether Windows reports anything to draw with. The same tablet list the
+ /// Finger drawing setting reads, which is a list of digitizers rather than
+ /// an answer about what is plugged in right now: a pen that has never been
+ /// brought into range can be absent from it.
+ ///
+ public static bool HasDrawingDevice()
+ {
+ foreach (TabletDevice device in Tablet.TabletDevices)
+ {
+ if (device.Type is TabletDeviceType.Stylus or TabletDeviceType.Touch)
+ {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void Window_PreviewKeyDown(object sender, KeyEventArgs e)
+ {
+ if (e.Key == Key.Escape)
+ {
+ Close();
+ e.Handled = true;
+ }
+ }
+
+ private void DiscussionLink_RequestNavigate(object sender, RequestNavigateEventArgs e)
+ {
+ Process.Start(new ProcessStartInfo(e.Uri.AbsoluteUri)
+ {
+ UseShellExecute = true,
+ });
+ e.Handled = true;
+ }
+
+ private void ContinueButton_Click(object sender, RoutedEventArgs e) => Close();
+}
diff --git a/src/SQLBI.Whiteboard/PreferencesWindow.xaml.cs b/src/SQLBI.Whiteboard/PreferencesWindow.xaml.cs
index 10b823a..ebaf79f 100644
--- a/src/SQLBI.Whiteboard/PreferencesWindow.xaml.cs
+++ b/src/SQLBI.Whiteboard/PreferencesWindow.xaml.cs
@@ -727,6 +727,7 @@ private ToggleButton CreateSwitch(SettingDescriptor setting)
IsChecked = setting.Id switch
{
SettingsCatalog.Ids.StartFullScreen => _settings.StartFullScreen,
+ SettingsCatalog.Ids.WarnWhenNoDigitizer => _settings.WarnWhenNoDigitizer,
SettingsCatalog.Ids.CheckForUpdates => _settings.CheckForUpdates,
_ => false,
},
@@ -904,6 +905,10 @@ private void SetBoolean(SettingDescriptor setting, bool value)
{
_settings.StartFullScreen = value;
}
+ else if (setting.Id == SettingsCatalog.Ids.WarnWhenNoDigitizer)
+ {
+ _settings.WarnWhenNoDigitizer = value;
+ }
else if (setting.Id == SettingsCatalog.Ids.CheckForUpdates)
{
_settings.CheckForUpdates = value;
diff --git a/src/SQLBI.Whiteboard/SettingsCatalog.cs b/src/SQLBI.Whiteboard/SettingsCatalog.cs
index 8cc4c78..ad4a80a 100644
--- a/src/SQLBI.Whiteboard/SettingsCatalog.cs
+++ b/src/SQLBI.Whiteboard/SettingsCatalog.cs
@@ -82,6 +82,7 @@ public static class Ids
public const string LaserTrailWeight = "laser.trailWeight";
public const string ToolbarPlacement = "toolbar.placement";
public const string ToolbarLayout = "toolbar.layout";
+ public const string WarnWhenNoDigitizer = "startup.noDigitizerNotice";
public const string FingerMode = "input.fingerMode";
public const string PenButton = "input.penButton";
public const string SnippetFormatOrder = "input.snippetFormatOrder";
@@ -118,6 +119,15 @@ public static class Ids
Editor = SettingEditorKind.BooleanSwitch,
},
new()
+ {
+ Id = Ids.WarnWhenNoDigitizer,
+ Category = Startup,
+ Title = "Warn when there is nothing to draw with",
+ Description = "Say so at startup when Windows reports neither a pen tablet nor a touchscreen. The application opens either way; a mouse pans, zooms, and moves containers, but cannot draw. What Windows reports is a list of digitizers rather than what is plugged in, so a pen that has never been brought into range can be missing from it.",
+ Keywords = ["pen", "touch", "touchscreen", "digitizer", "tablet", "mouse", "warning", "notice", "startup"],
+ Editor = SettingEditorKind.BooleanSwitch,
+ },
+ new()
{
Id = Ids.FingerMode,
Category = Input,
diff --git a/tests/SQLBI.Whiteboard.Core.SmokeTests/Program.cs b/tests/SQLBI.Whiteboard.Core.SmokeTests/Program.cs
index b5ec9d9..c0528a2 100644
--- a/tests/SQLBI.Whiteboard.Core.SmokeTests/Program.cs
+++ b/tests/SQLBI.Whiteboard.Core.SmokeTests/Program.cs
@@ -877,6 +877,14 @@ defaultSettings.StartupMonitorName is null &&
Assert(
defaultSettings.PenButtons.Barrel == PenButtonAction.Laser,
"Missing settings should assign Laser to the pen barrel button.");
+Assert(
+ defaultSettings.WarnWhenNoDigitizer,
+ "Missing settings should warn when Windows reports nothing to draw with.");
+Assert(
+ !AppSettingsSerializer.Parse(
+ AppSettingsSerializer.Format(new AppSettings { WarnWhenNoDigitizer = false }))
+ .WarnWhenNoDigitizer,
+ "Dismissing the no-digitizer notice should survive a round trip.");
Assert(
AppSettingsSerializer.Parse("{ \"penButtons\": { \"barrel\": \"Sideways\" } }")
.PenButtons.Barrel == PenButtonAction.Laser,