fix: clean up polygon previews and install script

This commit is contained in:
devmobasa
2026-06-01 16:42:23 +02:00
parent 5f8e616086
commit e9459a259a
9 changed files with 140 additions and 22 deletions
+1
View File
@@ -16,6 +16,7 @@ pub use background::{fill_transparent, render_board_background};
pub use blur::{BlurRectParams, render_blur_rect};
pub use highlight::render_click_highlight;
pub use pressure_strokes::render_freehand_pressure_borrowed;
pub(crate) use primitives::render_polygon_preview;
pub use selection::{render_selection_halo, render_selection_handles, selection_handle_rects};
pub use shapes::render_shape;
pub(crate) use strokes::render_eraser_stroke;
+57
View File
@@ -125,6 +125,39 @@ pub(super) fn render_polygon(
let _ = ctx.restore();
}
/// Render the in-progress freeform polygon preview without open-end round cap blobs.
#[allow(clippy::too_many_arguments)]
pub(crate) fn render_polygon_preview(
ctx: &cairo::Context,
points: &[(i32, i32)],
fill: bool,
color: Color,
thick: f64,
) {
if points.len() < 2 {
return;
}
let _ = ctx.save();
ctx.new_path();
ctx.set_source_rgba(color.r, color.g, color.b, color.a);
ctx.set_line_width(thick);
ctx.set_line_cap(cairo::LineCap::Butt);
ctx.set_line_join(cairo::LineJoin::Round);
ctx.move_to(points[0].0 as f64, points[0].1 as f64);
for &(x, y) in &points[1..] {
ctx.line_to(x as f64, y as f64);
}
if crate::draw::shape::has_minimum_distinct_points(points) {
ctx.close_path();
if fill {
let _ = ctx.fill_preserve();
}
}
let _ = ctx.stroke();
let _ = ctx.restore();
}
/// Render an arrow (line with arrowhead pointing towards the tip)
#[allow(clippy::too_many_arguments)]
pub(super) fn render_arrow(
@@ -204,6 +237,30 @@ mod tests {
surface.data().unwrap()[offset]
}
#[test]
fn polygon_preview_uses_butt_caps_for_open_edges() {
let (mut surface, ctx) = surface_with_context(90, 70);
let red = Color {
r: 1.0,
g: 0.0,
b: 0.0,
a: 1.0,
};
render_polygon_preview(&ctx, &[(20, 35), (70, 35)], false, red, 10.0);
drop(ctx);
assert_eq!(
alpha_at(&mut surface, 17, 35),
0,
"open polygon preview edges should not leave round endpoint blobs"
);
assert!(
alpha_at(&mut surface, 25, 35) > 0,
"open polygon preview edge should still render"
);
}
#[test]
fn ellipse_does_not_connect_to_existing_current_path() {
let (mut surface, ctx) = surface_with_context(120, 120);
+2
View File
@@ -2,6 +2,7 @@ use super::base::{DrawingState, InputState, TextInputMode};
use crate::draw::shape::{
bounding_box_for_points, bounding_box_for_sticky_note, bounding_box_for_text,
};
use crate::input::tool::PROVISIONAL_POLYGON_DAMAGE_PADDING;
use crate::util::Rect;
impl InputState {
@@ -51,6 +52,7 @@ impl InputState {
preview_points.push(point);
}
bounding_box_for_points(&preview_points, *thick)
.and_then(|rect| rect.inflated(PROVISIONAL_POLYGON_DAMAGE_PADDING))
}
_ => None,
}
+3 -17
View File
@@ -1,7 +1,6 @@
use crate::draw::render::render_freehand_pressure_borrowed;
use crate::draw::render::{render_freehand_pressure_borrowed, render_polygon_preview};
use crate::draw::{
Color, PolygonKind, Shape, render_freehand_borrowed, render_marker_stroke_borrowed,
render_shape,
Color, Shape, render_freehand_borrowed, render_marker_stroke_borrowed, render_shape,
};
use crate::input::Tool;
use crate::input::tool::{
@@ -189,20 +188,7 @@ impl InputState {
if let Some(point) = preview.or(Some((current_x, current_y))) {
preview_points.push(point);
}
if preview_points.len() >= 3 {
render_shape(
ctx,
&Shape::Polygon {
kind: PolygonKind::Freeform,
points: preview_points,
fill: *fill,
color: *color,
thick: *thick,
},
);
} else {
render_freehand_borrowed(ctx, &preview_points, *color, *thick);
}
render_polygon_preview(ctx, &preview_points, *fill, *color, *thick);
true
}
_ => false,
+20
View File
@@ -193,6 +193,26 @@ fn freeform_polygon_commit_records_first_stroke_onboarding() {
assert!(state.pending_onboarding_usage.first_stroke_done);
}
#[test]
fn freeform_polygon_preview_dirty_has_antialias_padding() {
let mut state = create_test_input_state();
assert!(state.set_tool_override(Some(Tool::FreeformPolygon)));
state.on_mouse_press(MouseButton::Left, 10, 10);
state.on_mouse_motion(20, 20);
let DrawingState::BuildingPolygon { thick, .. } = state.state else {
panic!("expected polygon building state");
};
let base = crate::draw::shape::bounding_box_for_points(&[(10, 10), (20, 20)], thick)
.expect("preview should have bounds");
assert_eq!(
state.last_provisional_bounds,
base.inflated(2),
"building polygon damage should be padded to clear antialias leftovers"
);
}
#[test]
fn freeform_polygon_freezes_style_on_first_click() {
let mut state = create_test_input_state();
+10 -1
View File
@@ -8,6 +8,8 @@ use crate::input::tool::{
};
use crate::util::{self, Rect};
pub(crate) const PROVISIONAL_POLYGON_DAMAGE_PADDING: i32 = 2;
/// Immutable inputs needed to turn one completed drag into an app-level outcome.
pub(crate) struct ToolStrokeSnapshot {
pub(crate) tool: Tool,
@@ -424,7 +426,14 @@ impl<'a> ProvisionalToolStroke<'a> {
bounding_box_for_points(points, inflated)
}
Self::EraserPreview { points, size } => bounding_box_for_eraser(points, *size),
Self::Shape(shape) => shape.bounding_box(),
Self::Shape(shape) => {
let bounds = shape.bounding_box();
if matches!(shape, Shape::Polygon { .. }) {
bounds.and_then(|rect| rect.inflated(PROVISIONAL_POLYGON_DAMAGE_PADDING))
} else {
bounds
}
}
Self::BlurReplayPreview(params) => {
bounding_box_for_blur(params.x, params.y, params.w, params.h)
}
+2 -2
View File
@@ -26,8 +26,8 @@ pub use drag::{DragBindableTool, DragTool};
)]
pub(crate) use drawing::ToolUsage;
pub(crate) use drawing::{
FinishedToolStroke, PolygonProvisionalSnapshot, PolygonStrokeSnapshot, ProvisionalToolSnapshot,
ProvisionalToolStroke, ToolStrokeSnapshot,
FinishedToolStroke, PROVISIONAL_POLYGON_DAMAGE_PADDING, PolygonProvisionalSnapshot,
PolygonStrokeSnapshot, ProvisionalToolSnapshot, ProvisionalToolStroke, ToolStrokeSnapshot,
};
pub use kind::Tool;
pub(crate) use profile::{ToolControlGroup, ToolProfile, ToolSettingsSlot, ToolSizeSource};
+26 -1
View File
@@ -1,7 +1,7 @@
use super::drawing::marker_color_with_opacity;
use super::*;
use crate::config::Action;
use crate::draw::Color;
use crate::draw::{Color, Shape};
use std::collections::HashSet;
fn color(r: f64) -> Color {
@@ -162,3 +162,28 @@ fn marker_opacity_helper_preserves_current_alpha_clamp() {
assert_eq!(marker_color_with_opacity(color(1.0), 0.0).a, 0.05);
assert_eq!(marker_color_with_opacity(color(1.0), 2.0).a, 0.9);
}
#[test]
fn provisional_polygon_bounds_include_extra_preview_padding() {
let stroke = Tool::Triangle.provisional_polygon_stroke(PolygonProvisionalSnapshot {
tool: Tool::Triangle,
start: (10, 10),
current: (60, 50),
color: color(1.0),
size: 4.0,
fill_enabled: false,
regular_sides: 5,
});
let ProvisionalToolStroke::Shape(shape @ Shape::Polygon { .. }) = &stroke else {
panic!("expected provisional polygon shape");
};
let base = shape
.bounding_box()
.expect("polygon preview should have bounds");
assert_eq!(
stroke.bounds(),
base.inflated(PROVISIONAL_POLYGON_DAMAGE_PADDING),
"polygon drag preview damage should clear antialias leftovers"
);
}
+19 -1
View File
@@ -92,6 +92,18 @@ echo ""
echo "✅ Installation complete!"
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Binary check"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo "Installed: $INSTALLED_BINARY"
ls -l "$INSTALLED_BINARY"
echo "SHA256: $(sha256sum "$INSTALLED_BINARY" | cut -d' ' -f1)"
if command -v "$BINARY_NAME" >/dev/null 2>&1; then
echo "PATH: $(command -v "$BINARY_NAME")"
else
echo "PATH: not found in PATH"
fi
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " Setup Instructions"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
@@ -172,7 +184,12 @@ case $REPLY in
# Enable and start the service
systemctl --user daemon-reload
systemctl --user enable wayscriber.service
systemctl --user start wayscriber.service
if systemctl --user restart wayscriber.service; then
echo "✅ Service restarted"
else
echo "⚠️ Restart failed; attempting start"
systemctl --user start wayscriber.service
fi
echo "✅ Service enabled and started"
echo ""
@@ -180,6 +197,7 @@ case $REPLY in
systemctl --user status wayscriber.service --no-pager -l
echo ""
echo "Commands:"
echo " Restart: systemctl --user restart wayscriber.service"
echo " Start: systemctl --user start wayscriber"
echo " Stop: systemctl --user stop wayscriber"
echo " Status: systemctl --user status wayscriber"