Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -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.
-->
<VersionPrefix>1.2.1</VersionPrefix>
<VersionPrefix>1.2.2</VersionPrefix>
<LangVersion>latest</LangVersion>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
Expand Down
7 changes: 4 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ How the project is developed and shipped is documented separately:
- Markdown `.wimport` recipes that build image and text containers from headings
- An intentionally small floating toolbar
- A File / Edit / View / Help tab strip. Click a tab for a one-row command strip over the canvas
- Preferences for the startup monitor, full-screen start, finger drawing, mouse drawing, the pen button, snippet format order, laser trail timing and weight, toolbar position and layout, and (except Store installs) a daily new-version check
- Preferences for the startup monitor, full-screen start, finger drawing, mouse drawing, the pen button, snippet format order, laser trail timing and weight, toolbar position and layout, keeping the Eraser on the toolbar for a pen that has no reverse end, and (except Store installs) a daily new-version check
- About, with version and channel

## Build and run
Expand Down Expand Up @@ -184,7 +184,7 @@ Use **Copy settings** after finding a useful combination so the exact values can
| Ctrl + left mouse | Select/move/resize a container and return to the previous drawing tool — what the left button does on its own when Mouse drawing is off |
| Double-click container | Center and fit the image, text, or LiveView to the canvas. With Mouse drawing on and an ink or eraser tool selected, hold Ctrl: two plain clicks are two strokes |
| Double-click empty canvas | Center and fit all board content, or reset an empty board |
| Pen eraser | Erase complete strokes. The upper side button erases too: Windows reports it the same way as a pen turned round |
| Pen eraser | Erase complete strokes. The upper side button erases too: Windows reports it the same way as a pen turned round. A pen with neither reaches the Eraser through **Help → Preferences → Toolbar → Always show the Eraser** |
| Pen barrel | Hold the barrel button for the action assigned in Preferences: Laser (default) or Straight line. Laser returns to the previous tool on release |
| One finger | Pan. With Finger drawing on, uses the current tool instead |
| Two fingers | Pan and pinch zoom. Cancels an in-progress finger stroke when Finger drawing is on |
Expand All @@ -205,7 +205,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, no-pen warning, finger drawing, mouse drawing, pen button, snippet format order, laser trail, toolbar, update checks |
| Help > Preferences | Searchable settings: startup monitor, full screen, no-pen warning, finger drawing, mouse drawing, pen button, snippet format order, laser trail, toolbar position and layout, always show the Eraser, update checks. Each setting is one line; its chevron opens the reasoning behind it. Search marks what it matched, and marks the chevron when the match is in the text behind it |
| 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 |
Expand Down Expand Up @@ -277,6 +277,7 @@ Test these on the target device before tuning stroke algorithms:
3. Rest a palm while drawing and verify the board does not pan.
4. Lift the pen, then immediately pan and pinch with touch.
5. Draw near all display edges and across the Windows display-scaling boundary, if multiple monitors use different scaling.
6. Turn on **Always show the Eraser**: it joins the row of tools, and moves to its own row under the palette when the layout is Dual palette. Pan does not appear either way, the tip erases while the Eraser is selected, and turning the setting back off returns to the last drawing tool.

Wacom driver settings can remap the barrel and eraser controls, so validate both Windows Ink mode and the intended application profile.

Expand Down
5 changes: 3 additions & 2 deletions TODO.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
<https://whiteboard.sqlbi.com> 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.2.1). Identity version for the Store package is `VersionPrefix.0` (`1.2.1.0`).
(1.2.2). Identity version for the Store package is `VersionPrefix.0` (`1.2.2.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
Expand All @@ -27,7 +27,8 @@ No numbered work remains. 1.2.0 answered
[discussion 78](https://github.com/sql-bi/SQLBI-Whiteboard/discussions/78) with Mouse
drawing — decision 23, with the alternatives kept in
[docs/mouse-mode.md](docs/mouse-mode.md) — and 1.2.1 made it discoverable from the
toolbar, decision 24. The video teaser is recorded and served from the landing page
toolbar, decision 24. 1.2.2 puts the Eraser within reach of a pen whose back end is not
one, as an option that is off by default. The video teaser is recorded and served from the landing page
itself as `site/teaser-av1.mp4` / `site/teaser-h264.mp4` — the Vimeo-embed plan was
reversed, see decision 19 in [docs/decisions.md](docs/decisions.md); the production
script and staging assets are in `docs/teaser/`. The release manifests and the Store
Expand Down
43 changes: 43 additions & 0 deletions docs/decisions.md
Original file line number Diff line number Diff line change
Expand Up @@ -512,6 +512,49 @@ can only have appeared on a machine where the automatic default already decided

---

## 25. Preferences says one line and keeps the rest behind a chevron

**Implemented** in 1.2.2. Every setting carries two texts rather than one: a `Summary` of a
single line that is always on the row, and a `Description` — the defaults, the reasoning,
the consequences — that appears only when the row's chevron is pressed. A setting whose
summary is the whole story leaves `Description` empty and gets no chevron at all.

The dialog had grown to where the longest description ran to six wrapped lines, and a
category was a wall of prose that had to be read to be skipped. The reasoning is worth
keeping — it is the difference between a setting someone can decide about and one they
guess at — so the answer was to stop showing it unasked rather than to delete it.

Two parts of this are choices a later change could quietly undo:

- **Not a tooltip.** A tooltip is the obvious way to hide text and the wrong one here. This
application is used with a pen and a finger, and neither hovers: on a Cintiq the
reasoning would simply be gone. The chevron is a real button because pressing is the one
gesture every input this application supports can perform.
- **Search marks what it matched.** Every hit is highlighted where it lies — in the title,
in the summary, in the prose once that is open. A hit lying only in the prose has nothing
on the row to mark, so the chevron is marked instead: it says the answer is in here
without opening a row under the hands of someone still typing. Expanding those rows
automatically was built first and then removed, because the list jumps on every
keystroke. A row matched only by a keyword marks nothing, deliberately — the word is not
in the prose either, and a marked chevron would promise text that is not there.

The chevron sits under the title and in front of the summary. The right-hand edge of the
row was tried first and cannot have it: editors range from a switch to a wide combo, so the
chevron landed somewhere different on every row; it took the width the summary needed to
stay on one line; and on **Snippet format order** it came to rest in the same column as
that editor's own reordering chevrons, where it read as one of them.

**Always show the Eraser is drawn rather than switched.** It is one boolean and a switch was
the ordinary answer, but the question it asks is where a button appears, so it is offered as
two pictures of the toolbar — identical but for the Eraser, and redrawn to match whichever
arrangement **Layout** has chosen, since that setting decides whether the Eraser joins the
bar or takes a row beneath it. The Eraser is in the accent color because the difference
between the two pictures is the whole question, and the Off picture reserves its space
rather than closing up, so that turning it on adds the Eraser instead of moving everything
else.

---

## Open questions

- arm64 is not built; add it if Surface devices matter for a pen application.
Expand Down
10 changes: 9 additions & 1 deletion src/SQLBI.Whiteboard.Core/Settings/AppSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,14 @@ public sealed class AppSettings
/// </summary>
public bool SuggestMouseMode { get; set; } = true;

/// <summary>
/// Whether the Eraser button stays on the toolbar when nothing else puts it
/// there. Off by default because the pen's reverse end already erases and
/// the row costs the toolbar its height; on for the pens that have no
/// reverse end, which otherwise cannot reach the Eraser at all.
/// </summary>
public bool ShowEraserButton { get; set; }

public List<string> SnippetFormatOrder { get; set; } = [.. TextLanguageIds.All];

public InkToolSettings Pen { get; set; } = InkToolSettings.From(InkPalettes.DefaultPen);
Expand Down Expand Up @@ -162,7 +170,7 @@ public sealed class AppSettings

public static class AppSettingsSerializer
{
public const int CurrentVersion = 14;
public const int CurrentVersion = 15;

private static readonly JsonSerializerOptions JsonOptions = new()
{
Expand Down
85 changes: 73 additions & 12 deletions src/SQLBI.Whiteboard/MainWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1639,10 +1639,6 @@ private static bool HasStylusDigitizer()
return false;
}

// Finger drawing and mouse drawing are separate settings that need the same
// two toolbar buttons, for the same reason: erasing is the pen's reverse end
// and panning is touch or Space, and a device with neither has nowhere else
// to reach them.
private void ApplyPointerModes()
{
var fingerInk = IsFingerModeEffective;
Expand All @@ -1652,19 +1648,79 @@ private void ApplyPointerModes()
}

InkSurface.SetAllowTouchInk(fingerInk);
ApplyExtraTools();
}

// Finger drawing and mouse drawing are separate settings that need the same
// two toolbar buttons, for the same reason: erasing is the pen's reverse end
// and panning is touch or Space, and a device with neither has nowhere else
// to reach them. The Eraser has a third reason of its own - a pen whose back
// end is not an eraser - so it can be asked for on its own, and Pan cannot.
private void ApplyExtraTools()
{
var extraTools = IsFingerModeEffective || IsMouseModeEffective;
var eraserTool = extraTools || _settings.ShowEraserButton;
var dual = IsDualLayout;
PlaceEraserButton(dual);
if (EraserToolButton is not null)
{
EraserToolButton.Visibility = eraserTool ? Visibility.Visible : Visibility.Collapsed;
}

if (PanToolButton is not null)
{
PanToolButton.Visibility = extraTools ? Visibility.Visible : Visibility.Collapsed;
}

var extraTools = fingerInk || IsMouseModeEffective;
if (ExtraToolsRow is not null)
{
ExtraToolsRow.Visibility = extraTools ? Visibility.Visible : Visibility.Collapsed;
// The row is Pan's alone once the Eraser has moved up into the bar,
// so it goes away with Pan rather than with either button.
var wanted = dual ? eraserTool : extraTools;
ExtraToolsRow.Visibility = wanted ? Visibility.Visible : Visibility.Collapsed;
}

if (!extraTools && _activeTool is BoardTool.Eraser or BoardTool.Pan)
if ((!eraserTool && _activeTool is BoardTool.Eraser) ||
(!extraTools && _activeTool is BoardTool.Pan))
{
SetActiveTool(_lastDrawingTool);
}
}

// Where the Eraser sits is a question about the layout, not about why it is
// there. The dual palette is stacked groups already, so a row beneath it
// reads as one more group; the compact bar is a single line of tools, and a
// second line holding one button doubles the toolbar's height to say very
// little.
private void PlaceEraserButton(bool dual)
{
if (EraserToolButton is null || ToolButtonsRow is null || ExtraToolsRow is null)
{
return;
}

Panel host = dual ? ExtraToolsRow : ToolButtonsRow;
if (ReferenceEquals(EraserToolButton.Parent, host))
{
return;
}

if (EraserToolButton.Parent is Panel previous)
{
previous.Children.Remove(EraserToolButton);
}

if (dual)
{
// Ahead of Pan, which is the order the two have always been in.
host.Children.Insert(0, EraserToolButton);
}
else
{
host.Children.Add(EraserToolButton);
}
}

private bool TryBeginFingerTool(StylusDownEventArgs e)
{
InkSurface.RegisterTouchTablet(e.StylusDevice.TabletDevice.Id);
Expand Down Expand Up @@ -2912,14 +2968,19 @@ private void ApplyCalligraphyAccess()
{
SetInkOptionsOpen(false);
RebuildDualPalette();
return;
}

ApplyInkOptionsWidth();
if (_isInkOptionsOpen)
else
{
RebuildInkOptions();
ApplyInkOptionsWidth();
if (_isInkOptionsOpen)
{
RebuildInkOptions();
}
}

// The Eraser sits in the bar in the compact layouts and under it in the
// dual palette, so changing the layout moves it.
ApplyExtraTools();
}

private void ApplyInkOptionsWidth()
Expand Down
15 changes: 13 additions & 2 deletions src/SQLBI.Whiteboard/PreferencesWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="Preferences"
Width="720"
Width="820"
Height="560"
MinWidth="560"
MinWidth="640"
MinHeight="400"
WindowStartupLocation="CenterOwner"
ResizeMode="CanResize"
Expand All @@ -21,12 +21,23 @@

<Border Style="{StaticResource SettingsSearchHost}">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<TextBlock x:Name="SearchPlaceholder"
Style="{StaticResource SettingsPlaceholder}"
Text="Search settings" />
<TextBox x:Name="SearchBox"
Style="{StaticResource SettingsSearchBox}"
TextChanged="SearchBox_TextChanged" />
<Button x:Name="SearchClear"
Grid.Column="1"
Style="{StaticResource SettingsClearButton}"
ToolTip="Clear the search"
AutomationProperties.Name="Clear the search"
Visibility="Collapsed"
Click="SearchClear_Click" />
</Grid>
</Border>

Expand Down
Loading