use eframe::egui::{
self, Align2, Color32, FontId, Key, PointerButton, Pos2, Rect, Sense, TextureHandle, Ui,
Vec2,
};
use crate::annotate::Tool;
use crate::editor::Editor;
use crate::editor::geometry::{edges_cursor, region_handle_at};
use crate::editor::state::{EditorState, ItemDragKind, RegionMode};
use crate::ui::paint;
pub fn show(ui: &mut Ui, editor: &mut Editor, texture: Option<&TextureHandle>) -> Rect {
let ctx = ui.ctx().clone();
let (response, painter) = ui.allocate_painter(ui.available_size(), Sense::click_and_drag());
let canvas = response.rect;
let measure = |text: &str, size: f32| -> Vec2 {
ctx.fonts_mut(|f| {
f.layout_no_wrap(text.to_owned(), FontId::proportional(size), Color32::WHITE).size()
})
};
if !editor.view.fitted {
editor.view.fit(editor.doc.image_size(), canvas.size());
}
if response.hovered() {
let (scroll, pinch) = ctx.input(|i| (i.smooth_scroll_delta.y, i.zoom_delta()));
let mut factor = pinch;
if scroll.abs() > 0.1 {
factor *= (scroll * 0.003).exp();
}
if factor != 1.0
&& let Some(pointer) = response.hover_pos()
{
editor.dismiss_transient();
editor.view.zoom_about(canvas, pointer, factor);
}
}
if response.dragged_by(PointerButton::Middle) && !editor.state.is_pointer_op() {
editor.view.pan += response.drag_delta();
}
let space_down = !editor.state.is_text_editing() && ctx.input(|i| i.key_down(Key::Space));
if space_down
&& response.drag_started_by(PointerButton::Primary)
&& editor.state.is_idle()
{
editor.begin_space_pan();
}
if matches!(editor.state, EditorState::Panning)
&& response.dragged_by(PointerButton::Primary)
{
editor.view.pan += response.drag_delta();
}
let primary_started = response.drag_started_by(PointerButton::Primary);
let secondary_started = response.drag_started_by(PointerButton::Secondary);
if !space_down
&& (primary_started || secondary_started)
&& let Some(pos) = ctx
.input(|i| i.pointer.press_origin())
.or_else(|| response.interact_pointer_pos())
{
let p = editor.view.to_image(canvas, pos);
let mods = ctx.input(|i| i.modifiers);
if secondary_started {
editor.secondary_drag_start(p);
} else {
editor.primary_drag_start(p, pos, canvas, mods, &measure);
}
}
if (response.dragged_by(PointerButton::Primary)
|| response.dragged_by(PointerButton::Secondary))
&& !matches!(editor.state, EditorState::Panning)
&& let Some(pos) = response.interact_pointer_pos()
{
editor.pointer_moved(editor.view.to_image(canvas, pos));
}
if response.drag_stopped() {
editor.pointer_up(&measure);
}
if editor.state.is_pointer_op() && !response.dragged() && !ctx.input(|i| i.pointer.any_down())
{
editor.cancel_pointer_op();
}
if response.clicked_by(PointerButton::Primary)
&& !space_down
&& let Some(pos) = response.interact_pointer_pos()
{
let p = editor.view.to_image(canvas, pos);
let mods = ctx.input(|i| i.modifiers);
editor.click(p, pos, canvas, mods, &measure);
}
if response.double_clicked_by(PointerButton::Primary)
&& let Some(pos) = response.interact_pointer_pos()
{
editor.double_click(editor.view.to_image(canvas, pos), &measure);
}
let hovered_item = response
.hover_pos()
.and_then(|pos| editor.hovered_item(editor.view.to_image(canvas, pos), &measure));
let sel_screen = editor.doc.region.map(|r| editor.view.rect_to_screen(canvas, r));
let cursor = match &editor.state {
EditorState::ItemsDrag { kind, .. } => match kind {
ItemDragKind::Move | ItemDragKind::Rotate => egui::CursorIcon::Grabbing,
ItemDragKind::Resize { edges } => edges_cursor(*edges),
ItemDragKind::ScaleUniform => egui::CursorIcon::ResizeNwSe,
ItemDragKind::Endpoint { .. } => egui::CursorIcon::Crosshair,
},
EditorState::RegionDraw { .. } => egui::CursorIcon::Crosshair,
EditorState::RegionAdjust { mode, .. } => match mode {
RegionMode::Move { .. } => egui::CursorIcon::Grabbing,
RegionMode::Resize { edges } => edges_cursor(*edges),
},
EditorState::DrawingShape { .. } | EditorState::RubberBand { .. } => {
egui::CursorIcon::Crosshair
}
EditorState::Panning => egui::CursorIcon::Grabbing,
EditorState::Idle | EditorState::TextEditing(_) | EditorState::ConfirmDiscard { .. } => {
if space_down {
egui::CursorIcon::Grab
} else if let Some(pos) = response.hover_pos() {
if let Some(kind) = editor.handle_under(pos, canvas, &measure) {
match kind {
ItemDragKind::Resize { edges } => edges_cursor(edges),
ItemDragKind::ScaleUniform => egui::CursorIcon::ResizeNwSe,
ItemDragKind::Rotate => egui::CursorIcon::Grab,
ItemDragKind::Move => egui::CursorIcon::Move,
ItemDragKind::Endpoint { .. } => egui::CursorIcon::Crosshair,
}
} else if editor.tool == Tool::Select
&& let Some(edges) = sel_screen.and_then(|ss| region_handle_at(ss, pos))
{
edges_cursor(edges)
} else if editor.tool == Tool::Select {
if hovered_item.is_some() {
egui::CursorIcon::Move
} else if sel_screen.is_some_and(|ss| ss.contains(pos)) {
egui::CursorIcon::Grab
} else {
egui::CursorIcon::Crosshair
}
} else {
egui::CursorIcon::Crosshair
}
} else {
egui::CursorIcon::Default
}
}
};
ctx.set_cursor_icon(cursor);
let img_screen = editor.view.rect_to_screen(canvas, editor.doc.image_rect());
if let Some(texture) = texture {
painter.image(
texture.id(),
img_screen,
Rect::from_min_max(Pos2::ZERO, Pos2::new(1.0, 1.0)),
Color32::WHITE,
);
}
let clipped = painter.with_clip_rect(img_screen.intersect(canvas));
let editing_id = editor.editing_target();
for (id, ann) in editor.doc.annotations() {
if editing_id == Some(*id) {
continue;
}
paint::paint_annotation(&clipped, &editor.view, canvas, ann);
}
paint::paint_drag_preview(&clipped, editor, canvas);
if editor.tool == Tool::Marker
&& editor.state.is_idle()
&& !space_down
&& let Some(pos) = response.hover_pos()
{
let radius = crate::annotate::marker_radius(&editor.style) * editor.view.zoom;
painter.circle_filled(pos, radius, editor.style.color.gamma_multiply(0.45));
painter.text(
pos,
Align2::CENTER_CENTER,
editor.doc.marker_next.to_string(),
FontId::proportional(editor.style.font_size * editor.view.zoom),
Color32::WHITE.gamma_multiply(0.6),
);
}
paint::paint_region(&painter, editor, canvas);
if let Some(id) = hovered_item
&& !editor.selected.contains(&id)
&& let Some(ann) = editor.doc.get(id)
{
paint::paint_hover_outline(&painter, editor, canvas, ann, &measure);
}
if matches!(editor.state, EditorState::RegionDraw { .. })
&& let Some(ss) = sel_screen
{
paint::paint_crosshairs(&painter, canvas, &[ss.min.x, ss.max.x], &[ss.min.y, ss.max.y]);
} else if editor.doc.region.is_none()
&& editor.state.is_idle()
&& !space_down
&& let Some(pos) = response.hover_pos()
{
paint::paint_crosshairs(&painter, canvas, &[pos.x], &[pos.y]);
}
paint::paint_selection_chrome(&painter, editor, canvas, &measure);
paint::paint_rubber_band(&painter, editor, canvas);
canvas
}