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
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ The v0.9.23+ prebuilt `wayscriber` packages require glibc 2.39 and GTK 4.12 —
### Drawing and editing
- Freehand pen, highlighter, eraser (circle/rect)
- Shapes: lines, rectangles, ellipses, polygons (with fill toggle)
- Arrows with optional auto-numbered labels; step markers for walkthroughs
- Arrows in four styles - standard, pointy, curved (drag its handle to route around what is in the way), and double-ended - with optional auto-numbered labels; step markers for walkthroughs
- Blur tool with four styles: soften, pixelate, secure (flattens the region to one color), and black out
- Spotlight tool: dims everything except the regions you draw, with optional 1×–4× magnification
- Multiline text and sticky notes with smoothing
Expand Down Expand Up @@ -1023,6 +1023,7 @@ pick_screen_color = ["I"]
Notes:

- Arrow labels can auto-number when enabled in the arrow toolbar; reset with <kbd>Ctrl+Shift+R</kbd>.
- Arrow style (standard, pointy, curved, double) is set from the arrow toolbar's style button, or by the **Cycle Arrow Style** command - which restyles selected arrows when there are any, and otherwise sets the style for the next arrow. It has no default shortcut; bind `cycle_arrow_style` in `config.toml` for direct keyboard access, and set `[arrow] style` there to pick which style new arrows start with. A selected curved arrow shows a round handle at the middle of its arc: drag it to reshape the curve, holding <kbd>Shift</kbd> to snap.
- Step markers auto-increment and reset from the toolbar (or bind `reset_step_markers` in `config.toml`).
- Preset slots can be saved/cleared from the toolbar; the slot changes right away and is written back to `config.toml` on a background worker, with the toast confirming it once the file has it. Edit names and advanced fields in the configurator's Presets tab.
- The blur tool has no default keyboard shortcut; bind `select_blur_tool` in `config.toml` if you want direct keyboard access.
Expand Down
10 changes: 10 additions & 0 deletions config.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ toggle_eraser_mode = ["Ctrl+Shift+E"]
select_spotlight_tool = []
# Step the blur tool through blur/pixelate/secure/black-out (unbound by default)
cycle_blur_style = []
# Step the arrow style through standard/pointy/curved/double (unbound by default).
# With arrows selected it restyles those instead of the next arrow.
cycle_arrow_style = []
select_line_tool = []
select_rect_tool = []
select_ellipse_tool = []
Expand Down Expand Up @@ -1290,6 +1293,13 @@ angle_degrees = 26.0
# Place the arrowhead at the end of the line instead of the start
head_at_end = true

# Shape of the next arrow drawn: "standard", "pointy", "curved", or "double".
# Every arrow stores its own style, so this only seeds new ones — nothing
# already drawn is restyled, and the Cycle Arrow Style action changes it at
# runtime. "double" ignores head_at_end; "curved" starts with a gentle arc you
# can reshape by dragging the round handle at its midpoint.
style = "standard"

# ═══════════════════════════════════════════════════════════════════════════════
# PERFORMANCE SETTINGS
# ═══════════════════════════════════════════════════════════════════════════════
Expand Down
14 changes: 13 additions & 1 deletion configurator/src/app/pages/arrow.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use relm4::prelude::*;

use crate::messages::Message;
use crate::models::util::format_float;
use crate::models::{TabId, TextField, ToggleField};
use crate::models::{ArrowStyleOption, TabId, TextField, ToggleField};

use super::super::search::SearchArea;
use super::super::state::ConfiguratorApp;
Expand Down Expand Up @@ -41,6 +41,18 @@ pub(super) fn build(sender: &ComponentSender<ConfiguratorApp>) -> BuiltPage {
"Off draws the head at the start of the line instead.",
|app| app.draft.arrow_head_at_end,
|value| Message::ToggleChanged(ToggleField::ArrowHeadAtEnd, value),
)
.combo_row(
"Arrow style",
"Shape of the next arrow drawn. Every arrow keeps its own style, so \
changing this never restyles existing drawings.",
ArrowStyleOption::list(),
ArrowStyleOption::list()
.iter()
.map(|option| option.label().to_string())
.collect(),
|app| app.draft.arrow_style,
Message::ArrowStyleChanged,
);

page.finish()
Expand Down
6 changes: 6 additions & 0 deletions configurator/src/app/search/terms.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,8 +131,14 @@ pub(super) const ARROW_TERMS: &[&str] = &[
"arrow length px",
"arrow angle deg",
"place arrowhead at end of line",
"arrow style",
"length",
"angle",
"style",
"standard",
"pointy",
"curved",
"double",
];
pub(super) const HISTORY_MAIN_TERMS: &[&str] = &[
"history",
Expand Down
5 changes: 5 additions & 0 deletions configurator/src/app/search/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,11 @@ fn exact_static_section_labels_match_their_sections() {
TabId::Arrow,
SearchArea::Arrow,
),
("arrow style", TabId::Arrow, SearchArea::Arrow),
// Searching for the style you want, rather than for the control that
// sets it, is the likelier way in — nobody knows the row is called
// "Arrow style" until they have already found it.
("curved", TabId::Arrow, SearchArea::Arrow),
];

for (query, expected_tab, expected_area) in cases {
Expand Down
18 changes: 16 additions & 2 deletions configurator/src/app/update/fields/drawing.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::models::{
ColorMode, ColorPickerId, DragColorOption, DragMouseButton, DragToolField, DragToolOption,
EraserModeOption, NamedColorOption,
ArrowStyleOption, ColorMode, ColorPickerId, DragColorOption, DragMouseButton, DragToolField,
DragToolOption, EraserModeOption, NamedColorOption,
};

use super::super::super::effects::Effect;
Expand Down Expand Up @@ -180,6 +180,20 @@ impl ConfiguratorApp {
Vec::new()
}

/// `[arrow] style`: which shape the next arrow is drawn in.
///
/// Seeds new arrows only. Nothing already drawn is restyled, because every
/// arrow stores its own style.
pub(in crate::app::update) fn handle_arrow_style_changed(
&mut self,
option: ArrowStyleOption,
) -> Vec<Effect> {
self.status = StatusMessage::idle();
self.draft.arrow_style = option;
self.refresh_dirty_flag();
Vec::new()
}

pub(in crate::app::update) fn handle_drawing_mouse_drag_tool_changed(
&mut self,
button: DragMouseButton,
Expand Down
1 change: 1 addition & 0 deletions configurator/src/app/update/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ impl ConfiguratorApp {
self.handle_quick_named_color_selected(index, option)
}
Message::EraserModeChanged(option) => self.handle_eraser_mode_changed(option),
Message::ArrowStyleChanged(option) => self.handle_arrow_style_changed(option),
Message::DrawingDragMappingSectionToggled(button) => {
self.handle_drawing_drag_mapping_section_toggled(button)
}
Expand Down
11 changes: 6 additions & 5 deletions configurator/src/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ use std::path::PathBuf;
use wayscriber::config::{ConfigDocument, Shortcut, ToolbarItemId, ToolbarItemOrderGroup};

use crate::models::{
BoardBackgroundOption, BoardItemTextField, BoardItemToggleField, ColorMode, ColorPickerId,
DaemonAction, DaemonActionResult, DaemonRuntimeStatus, DragColorOption, DragMouseButton,
DragToolField, DragToolOption, EraserModeOption, FontStyleOption, FontWeightOption,
InputHudModeOption, InputHudPositionOption, KeybindingField, KeybindingsTabId,
KeyboardModifiers, NamedColorOption, OverrideOption, PdfFitModeOption,
ArrowStyleOption, BoardBackgroundOption, BoardItemTextField, BoardItemToggleField, ColorMode,
ColorPickerId, DaemonAction, DaemonActionResult, DaemonRuntimeStatus, DragColorOption,
DragMouseButton, DragToolField, DragToolOption, EraserModeOption, FontStyleOption,
FontWeightOption, InputHudModeOption, InputHudPositionOption, KeybindingField,
KeybindingsTabId, KeyboardModifiers, NamedColorOption, OverrideOption, PdfFitModeOption,
PdfLabelContentModeOption, PdfLabelPositionOption, PdfOrientationOption, PdfPageSizeOption,
PdfTransparentBackgroundOption, PresenterToolBehaviorOption, PresenterToolbarModeOption,
PresetEraserKindOption, PresetEraserModeOption, PresetTextField, PresetToggleField,
Expand Down Expand Up @@ -112,6 +112,7 @@ pub enum Message {
QuickColorModeChanged(usize, ColorMode),
QuickNamedColorSelected(usize, NamedColorOption),
EraserModeChanged(EraserModeOption),
ArrowStyleChanged(ArrowStyleOption),
DrawingDragMappingSectionToggled(DragMouseButton),
DrawingMouseDragToolChanged(DragMouseButton, DragToolField, DragToolOption),
DrawingMouseDragColorChanged(DragMouseButton, DragToolField, DragColorOption),
Expand Down
3 changes: 2 additions & 1 deletion configurator/src/models/config/draft/from_config.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::super::super::color::{ColorInput, ColorQuadInput};
use super::super::super::fields::ZoomChipDisplayOption;
use super::super::super::fields::{
EraserModeOption, FontStyleOption, FontWeightOption, InputHudModeOption,
ArrowStyleOption, EraserModeOption, FontStyleOption, FontWeightOption, InputHudModeOption,
InputHudPositionOption, PdfFitModeOption, PdfLabelContentModeOption, PdfLabelPositionOption,
PdfOrientationOption, PdfPageSizeOption, PdfTransparentBackgroundOption,
PresenterToolBehaviorOption, PresenterToolbarModeOption, ReducedMotionOption,
Expand Down Expand Up @@ -100,6 +100,7 @@ impl ConfigDraft {
arrow_length: format_float(config.arrow.length),
arrow_angle: format_float(config.arrow.angle_degrees),
arrow_head_at_end: config.arrow.head_at_end,
arrow_style: ArrowStyleOption::from_style(config.arrow.style),

history_undo_all_delay_ms: config.history.undo_all_delay_ms.to_string(),
history_redo_all_delay_ms: config.history.redo_all_delay_ms.to_string(),
Expand Down
3 changes: 2 additions & 1 deletion configurator/src/models/config/draft/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod from_config;
use super::super::color::{ColorInput, ColorQuadInput};
use super::super::fields::ZoomChipDisplayOption;
use super::super::fields::{
EraserModeOption, FontStyleOption, FontWeightOption, InputHudModeOption,
ArrowStyleOption, EraserModeOption, FontStyleOption, FontWeightOption, InputHudModeOption,
InputHudPositionOption, PdfFitModeOption, PdfLabelContentModeOption, PdfLabelPositionOption,
PdfOrientationOption, PdfPageSizeOption, PdfTransparentBackgroundOption,
PresenterToolBehaviorOption, PresenterToolbarModeOption, ReducedMotionOption,
Expand Down Expand Up @@ -60,6 +60,7 @@ pub struct ConfigDraft {
pub arrow_length: String,
pub arrow_angle: String,
pub arrow_head_at_end: bool,
pub arrow_style: ArrowStyleOption,

pub history_undo_all_delay_ms: String,
pub history_redo_all_delay_ms: String,
Expand Down
46 changes: 40 additions & 6 deletions configurator/src/models/config/tests.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
use super::super::color::ColorInput;
use super::super::fields::{
DragMouseButton, DragToolField, DragToolOption, FontWeightOption, InputHudModeOption,
InputHudPositionOption, OverrideOption, PdfFitModeOption, PdfLabelContentModeOption,
PdfOrientationOption, PdfPageSizeOption, PdfTransparentBackgroundOption, QuadField,
ReducedMotionOption, RegionPickerOption, SessionStorageModeOption, TextField, ToggleField,
ToolOption, ToolbarLayoutModeOption, ToolbarOverrideField, ToolbarRebindModifierOption,
UiThemeOption,
ArrowStyleOption, DragMouseButton, DragToolField, DragToolOption, FontWeightOption,
InputHudModeOption, InputHudPositionOption, OverrideOption, PdfFitModeOption,
PdfLabelContentModeOption, PdfOrientationOption, PdfPageSizeOption,
PdfTransparentBackgroundOption, QuadField, ReducedMotionOption, RegionPickerOption,
SessionStorageModeOption, TextField, ToggleField, ToolOption, ToolbarLayoutModeOption,
ToolbarOverrideField, ToolbarRebindModifierOption, UiThemeOption,
};

#[test]
Expand All @@ -28,6 +28,40 @@ fn config_draft_round_trips_toolbar_rebind_modifier() {
);
}

#[test]
fn config_draft_round_trips_arrow_style() {
// Without this the configurator is a one-way door for the key: a config
// that sets a style loads as Standard in the UI, and saving anything from
// any tab writes that Standard back over the user's choice.
let mut config = Config::default();
config.arrow.style = wayscriber::draw::ArrowStyle::Curved;
let mut draft = ConfigDraft::from_config(&config);
assert_eq!(draft.arrow_style, ArrowStyleOption::Curved);

draft.arrow_style = ArrowStyleOption::Double;
let round_trip = draft.to_config(&config).expect("arrow style round trip");
assert_eq!(round_trip.arrow.style, wayscriber::draw::ArrowStyle::Double);
}

#[test]
fn every_arrow_style_survives_the_configurator() {
// The combo row lists these by index, so a variant missing from either
// conversion arm silently maps to the wrong style rather than failing.
for option in ArrowStyleOption::list() {
let config = Config::default();
let mut draft = ConfigDraft::from_config(&config);
draft.arrow_style = option;
let round_trip = draft.to_config(&config).expect("arrow style round trip");
assert_eq!(round_trip.arrow.style, option.to_style());
assert_eq!(
ArrowStyleOption::from_style(round_trip.arrow.style),
option,
"{} did not survive the round trip",
option.label()
);
}
}

#[test]
fn config_draft_round_trips_status_bar_interactive() {
let config = Config::default();
Expand Down
1 change: 1 addition & 0 deletions configurator/src/models/config/to_config/drawing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ impl ConfigDraft {
|value| config.arrow.angle_degrees = value,
);
config.arrow.head_at_end = self.arrow_head_at_end;
config.arrow.style = self.arrow_style.to_style();
}
}

Expand Down
54 changes: 54 additions & 0 deletions configurator/src/models/fields/arrow.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
use wayscriber::draw::ArrowStyle;

/// Startup shape of the arrow tool, as a combo-row choice.
///
/// Wraps `ArrowStyle` rather than binding the core enum directly so the
/// configurator owns its own ordering and its own labels — the combo row lists
/// these in declaration order, which is not something the drawing code should
/// have to preserve.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArrowStyleOption {
Standard,
Pointy,
Curved,
Double,
}

impl ArrowStyleOption {
pub fn list() -> Vec<Self> {
vec![Self::Standard, Self::Pointy, Self::Curved, Self::Double]
}

pub fn label(&self) -> &'static str {
match self {
Self::Standard => "Standard",
Self::Pointy => "Pointy",
Self::Curved => "Curved",
Self::Double => "Double",
}
}

pub fn to_style(self) -> ArrowStyle {
match self {
Self::Standard => ArrowStyle::Standard,
Self::Pointy => ArrowStyle::Pointy,
Self::Curved => ArrowStyle::Curved,
Self::Double => ArrowStyle::Double,
}
}

pub fn from_style(style: ArrowStyle) -> Self {
match style {
ArrowStyle::Standard => Self::Standard,
ArrowStyle::Pointy => Self::Pointy,
ArrowStyle::Curved => Self::Curved,
ArrowStyle::Double => Self::Double,
}
}
}

impl std::fmt::Display for ArrowStyleOption {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.label())
}
}
2 changes: 2 additions & 0 deletions configurator/src/models/fields/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod arrow;
mod capture;
mod eraser;
mod export;
Expand All @@ -13,6 +14,7 @@ mod toggles;
mod tool;
mod toolbar;

pub use arrow::ArrowStyleOption;
pub use capture::RegionPickerOption;
pub use eraser::{EraserModeOption, PresetEraserKindOption, PresetEraserModeOption};
pub use export::{
Expand Down
17 changes: 17 additions & 0 deletions configurator/src/models/fields/tests.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,22 @@
use super::*;

#[test]
fn arrow_style_options_cover_the_core_enum() {
// `ArrowStyle::ALL` is what the runtime cycle steps through. If the two
// lists ever disagree, a style is reachable from the overlay but not from
// the configurator — or worse, the combo row offers one that no longer
// exists and picks the wrong index for everything after it.
let core = wayscriber::draw::ArrowStyle::ALL;
let options = ArrowStyleOption::list();
assert_eq!(options.len(), core.len());
for (option, style) in options.iter().zip(core.iter()) {
assert_eq!(option.to_style(), *style);
assert_eq!(ArrowStyleOption::from_style(*style), *option);
// The combo row and the overlay's style pill name the same thing.
assert_eq!(option.label(), style.label());
}
}

#[test]
fn region_picker_options_cover_the_core_enum() {
assert_eq!(
Expand Down
1 change: 1 addition & 0 deletions configurator/src/models/keybindings/field/config/read.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ impl KeybindingField {
Self::SelectBlurTool => &config.tools.select_blur_tool,
Self::SelectSpotlightTool => &config.tools.select_spotlight_tool,
Self::CycleBlurStyle => &config.tools.cycle_blur_style,
Self::CycleArrowStyle => &config.tools.cycle_arrow_style,
Self::SelectHighlightTool => &config.tools.select_highlight_tool,
Self::IncreaseFontSize => &config.tools.increase_font_size,
Self::DecreaseFontSize => &config.tools.decrease_font_size,
Expand Down
1 change: 1 addition & 0 deletions configurator/src/models/keybindings/field/config/write.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ impl KeybindingField {
Self::SelectBlurTool => config.tools.select_blur_tool = value,
Self::SelectSpotlightTool => config.tools.select_spotlight_tool = value,
Self::CycleBlurStyle => config.tools.cycle_blur_style = value,
Self::CycleArrowStyle => config.tools.cycle_arrow_style = value,
Self::SelectHighlightTool => config.tools.select_highlight_tool = value,
Self::IncreaseFontSize => config.tools.increase_font_size = value,
Self::DecreaseFontSize => config.tools.decrease_font_size = value,
Expand Down
1 change: 1 addition & 0 deletions configurator/src/models/keybindings/field/labels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ impl KeybindingField {
Self::SelectBlurTool => "select_blur_tool",
Self::SelectSpotlightTool => "select_spotlight_tool",
Self::CycleBlurStyle => "cycle_blur_style",
Self::CycleArrowStyle => "cycle_arrow_style",
Self::SelectHighlightTool => "select_highlight_tool",
Self::IncreaseFontSize => "increase_font_size",
Self::DecreaseFontSize => "decrease_font_size",
Expand Down
1 change: 1 addition & 0 deletions configurator/src/models/keybindings/field/list.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ impl KeybindingField {
Self::SelectBlurTool,
Self::SelectSpotlightTool,
Self::CycleBlurStyle,
Self::CycleArrowStyle,
Self::SelectHighlightTool,
Self::IncreaseFontSize,
Self::DecreaseFontSize,
Expand Down
1 change: 1 addition & 0 deletions configurator/src/models/keybindings/field/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ pub enum KeybindingField {
SelectBlurTool,
SelectSpotlightTool,
CycleBlurStyle,
CycleArrowStyle,
SelectHighlightTool,
IncreaseFontSize,
DecreaseFontSize,
Expand Down
1 change: 1 addition & 0 deletions configurator/src/models/keybindings/field/tab.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ impl KeybindingField {
| Self::SelectBlurTool
| Self::SelectSpotlightTool
| Self::CycleBlurStyle
| Self::CycleArrowStyle
| Self::SelectHighlightTool
| Self::ToggleHighlightTool
| Self::ResetArrowLabels
Expand Down
Loading
Loading