Skip to content

feat: image tool — insert and paste images into the screenshot - #563

Open
rokokol wants to merge 8 commits into
Satty-org:mainfrom
rokokol:feat/image-tool
Open

feat: image tool — insert and paste images into the screenshot#563
rokokol wants to merge 8 commits into
Satty-org:mainfrom
rokokol:feat/image-tool

Conversation

@rokokol

@rokokol rokokol commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Closes #233

This adds a new Image tool that makes it possible to insert pictures into the screenshot, addressing the concern raised in the issue: the pasted image is fully controllable (position, scale, rotation, mirroring) before it is rendered into the annotation stack.

2026-07-03.00-48-28.mp4

Usage

  • Selecting the tool (toolbar button or o) opens a file chooser; the chosen image is placed in the center of the screenshot, scaled down to at most half of the screenshot size (small images are not upscaled).
  • Ctrl+V pastes an image from the clipboard at any time: satty switches to the image tool and inserts it, skipping the file chooser. If the clipboard holds no image, nothing happens. Text editing is unaffected — the text tool keeps consuming its own Ctrl+V.
  • The image is edited with the familiar 8-handle frame (same look as crop), plus a rotate handle above the top edge:
    • dragging inside moves it,
    • corner handles resize it keeping the aspect ratio,
    • edge handles stretch it along one axis,
    • dragging a resize handle across the opposite side mirrors the image,
    • the rotate handle rotates it; Shift snaps to 15° steps (same as arrow/line),
    • the handle under the cursor is highlighted.
  • Enter or switching to another tool commits the image into the drawable stack (so undo/redo, clear-all and auto-copy work as with any other annotation); Escape discards it.

Configuration

  • New default keybind o, configurable via [keybinds] image = "…".
  • image is accepted by --initial-tool / initial-tool.
  • README and config.toml updated, new options marked NEXTRELEASE.

Implementation notes

  • The tool follows the existing patterns: an Image drawable + ImageTool implementing Tool, committed via ToolUpdateResult::Commit like the text tool.
  • Geometry is kept as center/rotation plus a signed size, where a negative component means the image is mirrored on that axis. This makes mirroring fall out of the resize math for free, and corners and edges share one code path (a zero component in the handle's sign vector leaves that axis unchanged).
  • The file chooser result and clipboard paste are delivered through a new SketchBoardInput::ImageSelected(Pixbuf)ToolEvent::ImageSelected message, mirroring how text commit events are routed. On paste the image is handed to the tool before activation so that activation does not open the file chooser.
  • Two small refactors to avoid duplication: the crop tool's handle drawing moved to drag_box (now shared, with an optional highlight), and the renderer-private background upload became a reusable create_image_from_pixbuf.
  • PointerPos events now get converted to image coordinates like the other mouse events (needed for hover tracking); the renderer keeps using the raw position via the already existing screen_pos field.
  • The FileChooserNative is stored in the tool until the response arrives — GTK does not hold a reference to native dialogs, and dropping the wrapper while the dialog is open crashes deep inside the file chooser internals.

Testing

  • Manually tested on Wayland (NixOS): inserting from file and from clipboard, moving, resizing from all 8 handles, mirroring, rotating with and without snapping, committing via Enter/tool switch/right-click actions, discarding via Escape, undo/redo of committed images, output via clipboard and file.
  • cargo fmt, cargo clippy --all-targets --all-features -- -D warnings and cargo test are clean.

Note: building currently requires a newer toolchain than the pinned rust-overlay in flake.lock provides (rustc ≥ 1.92 for relm4 0.10.1 and friends) — that predates this PR; I built with nix develop --override-input rust-overlay github:oxalica/rust-overlay.

@RobertMueller2

Copy link
Copy Markdown
Member

Thank you for the PR, this will be a great addition to Satty. But it's also going to take a bit to review :)

Just a heads up, I think we may merge #555, #561 and #562 to main first. The former definitely overlaps because it changes how shortcut keybindings work and would require some editing here. The other two might or might not cause a conflict.

rokokol added 5 commits July 3, 2026 10:11
Move the crop tool's handle drawing into drag_box and turn the
renderer-private background upload into a reusable
create_image_from_pixbuf, so other tools can draw handles and upload
pixbufs to the canvas.
Selecting the tool opens a file chooser and places the chosen image in
the center of the screenshot, scaled down to at most half of its size.
The image is edited with the familiar 8-handle frame plus a rotate
handle: dragging inside moves it, corner handles resize it keeping the
aspect ratio, edge handles stretch it along one axis and dragging a
handle across the opposite side mirrors the image. Shift snaps the
rotation to 15 degree steps and the handle under the cursor is
highlighted. Enter or switching to another tool renders the image into
the screenshot, Escape discards it.
If the clipboard contains an image, Ctrl+V switches to the image tool
and inserts it, without opening the file chooser. Text editing keeps
its own Ctrl+V handling since the text tool stops propagation.
gdk-pixbuf only handles webp when the external webp-pixbuf-loader is
installed. Route all image decoding through a shared helper that tries
gdk-pixbuf first and falls back to the image crate, which is already in
the dependency tree via femtovg, with only the pure-Rust webp decoder
enabled. This covers the input file, stdin and the image tool alike.
Accept file and texture drops on the sketch board and insert them
through the same path as clipboard paste, centering the image on the
drop position.
@rokokol
rokokol force-pushed the feat/image-tool branch from 7630661 to 617d060 Compare July 3, 2026 07:20
rokokol added 3 commits July 3, 2026 11:37
On Wayland the drag position arrives as (0,0) until the first motion
event and gtk picks the drop widget chain there, which is usually the
tools toolbar. With the drop target on the sketch board area the drop
never got accepted and the compositor cancelled the drag before any
motion arrived. Move the target to the window, which is part of every
pick chain, translate the drop position into canvas coordinates and
clamp it to the image so a drop released over a toolbar stays visible.
The explicit enter/motion handlers are required as well: the default
ones report no action before the data is available, which equally
cancels the drag.
Copying a file in a file manager puts a uri list on the clipboard, not
image data, so Ctrl+V silently did nothing; fall back to reading the
clipboard as a file list through the same loader the drop path uses.
The pasted image now also centers on the last pointer position instead
of the middle of the screenshot, falling back to the center when the
pointer is outside the window.
@rokokol

rokokol commented Jul 3, 2026

Copy link
Copy Markdown
Contributor Author

Thank you! :D

I've merged with my other PR #565 and added some enhancements like drag & drop and webp processing

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Feature]: Paste images from clipboard into satty

2 participants