use crate::session::{FocusTarget, FormSession};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct FocusChange {
pub from: Option<FocusTarget>,
pub to: Option<FocusTarget>,
pub commit_outgoing: bool,
pub clear_undo: bool,
}
impl FocusChange {
#[must_use]
pub fn moved(self) -> bool {
self.from != self.to
}
#[must_use]
pub fn none(at: Option<FocusTarget>) -> FocusChange {
FocusChange {
from: at,
to: at,
commit_outgoing: false,
clear_undo: false,
}
}
}
pub fn set(session: &mut FormSession, target: FocusTarget) -> FocusChange {
let from = session.focus;
if from == Some(target) {
return FocusChange::none(from);
}
let commit_outgoing = from.is_some_and(|f| f.field().is_some());
let clear_undo = match (from.and_then(FocusTarget::field), target.field()) {
(Some(old), Some(new)) => old != new,
(Some(_), None) => true,
_ => false,
};
session.focus = Some(target);
session.drag = None;
FocusChange {
from,
to: Some(target),
commit_outgoing,
clear_undo,
}
}
pub fn kill(session: &mut FormSession) -> FocusChange {
let from = session.focus;
if from.is_none() {
return FocusChange::none(None);
}
session.focus = None;
session.drag = None;
FocusChange {
from,
to: None,
commit_outgoing: from.is_some_and(|f| f.field().is_some()),
clear_undo: from.is_some_and(|f| f.field().is_some()),
}
}
#[must_use]
pub fn miss_drops_focus(button: crate::event::Button) -> bool {
match button {
crate::event::Button::Left => true,
crate::event::Button::Right => false,
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::event::Button;
use crate::session::{AnnotId, FieldId};
fn widget(field: u32, annot: u32) -> FocusTarget {
FocusTarget::Widget(FieldId(field), AnnotId::new(0, annot))
}
#[test]
fn focusing_from_nothing_takes_focus_and_commits_nothing() {
let mut session = FormSession::new();
let change = set(&mut session, widget(0, 1));
assert_eq!(session.focus, Some(widget(0, 1)));
assert_eq!(change.from, None);
assert!(change.moved());
assert!(!change.commit_outgoing, "there was nothing to commit");
assert!(!change.clear_undo);
}
#[test]
fn refocusing_the_holder_changes_nothing() {
let mut session = FormSession::new();
set(&mut session, widget(0, 1));
let change = set(&mut session, widget(0, 1));
assert!(!change.moved());
assert!(!change.commit_outgoing, "no commit on a re-focus");
assert!(!change.clear_undo, "and the history survives");
assert_eq!(session.focus, Some(widget(0, 1)));
}
#[test]
fn moving_between_fields_commits_and_clears() {
let mut session = FormSession::new();
set(&mut session, widget(0, 1));
let change = set(&mut session, widget(1, 2));
assert!(change.moved());
assert!(change.commit_outgoing);
assert!(change.clear_undo);
assert_eq!(change.from, Some(widget(0, 1)));
}
#[test]
fn moving_between_widgets_of_one_field_keeps_the_history() {
let mut session = FormSession::new();
set(&mut session, widget(0, 1));
let change = set(&mut session, widget(0, 5));
assert!(change.moved());
assert!(
!change.clear_undo,
"the field is the same, so its history stands"
);
}
#[test]
fn killing_focus_commits_the_outgoing_field() {
let mut session = FormSession::new();
set(&mut session, widget(0, 1));
let change = kill(&mut session);
assert_eq!(session.focus, None);
assert_eq!(change.from, Some(widget(0, 1)));
assert_eq!(change.to, None);
assert!(change.commit_outgoing);
assert!(change.clear_undo);
}
#[test]
fn killing_focus_twice_is_harmless() {
let mut session = FormSession::new();
set(&mut session, widget(0, 1));
kill(&mut session);
let second = kill(&mut session);
assert!(!second.moved());
assert!(!second.commit_outgoing);
assert_eq!(session.focus, None);
}
#[test]
fn a_plain_annotation_holds_focus_without_a_value() {
let mut session = FormSession::new();
let change = set(&mut session, FocusTarget::Annot(AnnotId::new(0, 4)));
assert_eq!(session.focus, Some(FocusTarget::Annot(AnnotId::new(0, 4))));
assert!(!change.commit_outgoing);
let away = kill(&mut session);
assert!(
!away.commit_outgoing,
"a link has no value to store on the way out"
);
}
#[test]
fn leaving_a_field_for_an_annotation_commits_it() {
let mut session = FormSession::new();
set(&mut session, widget(0, 1));
let change = set(&mut session, FocusTarget::Annot(AnnotId::new(0, 4)));
assert!(change.commit_outgoing);
assert!(change.clear_undo);
}
#[test]
fn only_the_left_button_drops_focus_on_a_miss() {
assert!(miss_drops_focus(Button::Left));
assert!(!miss_drops_focus(Button::Right));
}
#[test]
fn a_focus_change_ends_a_drag() {
use crate::edit::Place;
use crate::session::DragAnchor;
let mut session = FormSession::new();
set(&mut session, widget(0, 1));
session.drag = Some(DragAnchor {
field: FieldId(0),
start: Place::start(),
});
set(&mut session, widget(1, 2));
assert!(session.drag.is_none());
}
}