mirror of
https://github.com/devmobasa/wayscriber.git
synced 2026-06-03 03:54:42 +02:00
fix: clean up polygon previews and install script
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -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,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,
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user