271 lines
5.5 KiB
Rust
271 lines
5.5 KiB
Rust
use crate::Selector;
|
|
use crate::core::widget::operation::{
|
|
Focusable, Outcome, Scrollable, TextInput,
|
|
};
|
|
use crate::core::widget::{Id, Operation};
|
|
use crate::core::{Rectangle, Vector};
|
|
use crate::target::Candidate;
|
|
|
|
use std::any::Any;
|
|
|
|
pub type Find<S> = Finder<One<S>>;
|
|
pub type FindAll<S> = Finder<All<S>>;
|
|
|
|
#[derive(Debug)]
|
|
pub struct One<S>
|
|
where
|
|
S: Selector,
|
|
{
|
|
selector: S,
|
|
output: Option<S::Output>,
|
|
}
|
|
|
|
impl<S> One<S>
|
|
where
|
|
S: Selector,
|
|
{
|
|
pub fn new(selector: S) -> Self {
|
|
Self {
|
|
selector,
|
|
output: None,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S> Strategy for One<S>
|
|
where
|
|
S: Selector,
|
|
S::Output: Clone,
|
|
{
|
|
type Output = Option<S::Output>;
|
|
|
|
fn feed(&mut self, target: Candidate<'_>) {
|
|
if let Some(output) = self.selector.select(target) {
|
|
self.output = Some(output);
|
|
}
|
|
}
|
|
|
|
fn is_done(&self) -> bool {
|
|
self.output.is_some()
|
|
}
|
|
|
|
fn finish(&self) -> Self::Output {
|
|
self.output.clone()
|
|
}
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct All<S>
|
|
where
|
|
S: Selector,
|
|
{
|
|
selector: S,
|
|
outputs: Vec<S::Output>,
|
|
}
|
|
|
|
impl<S> All<S>
|
|
where
|
|
S: Selector,
|
|
{
|
|
pub fn new(selector: S) -> Self {
|
|
Self {
|
|
selector,
|
|
outputs: Vec::new(),
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S> Strategy for All<S>
|
|
where
|
|
S: Selector,
|
|
S::Output: Clone,
|
|
{
|
|
type Output = Vec<S::Output>;
|
|
|
|
fn feed(&mut self, target: Candidate<'_>) {
|
|
if let Some(output) = self.selector.select(target) {
|
|
self.outputs.push(output);
|
|
}
|
|
}
|
|
|
|
fn is_done(&self) -> bool {
|
|
false
|
|
}
|
|
|
|
fn finish(&self) -> Self::Output {
|
|
self.outputs.clone()
|
|
}
|
|
}
|
|
|
|
pub trait Strategy {
|
|
type Output;
|
|
|
|
fn feed(&mut self, target: Candidate<'_>);
|
|
|
|
fn is_done(&self) -> bool;
|
|
|
|
fn finish(&self) -> Self::Output;
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct Finder<S> {
|
|
strategy: S,
|
|
stack: Vec<(Rectangle, Vector)>,
|
|
viewport: Rectangle,
|
|
translation: Vector,
|
|
}
|
|
|
|
impl<S> Finder<S> {
|
|
pub fn new(strategy: S) -> Self {
|
|
Self {
|
|
strategy,
|
|
stack: vec![(Rectangle::INFINITE, Vector::ZERO)],
|
|
viewport: Rectangle::INFINITE,
|
|
translation: Vector::ZERO,
|
|
}
|
|
}
|
|
}
|
|
|
|
impl<S> Operation<S::Output> for Finder<S>
|
|
where
|
|
S: Strategy + Send,
|
|
S::Output: Send,
|
|
{
|
|
fn traverse(
|
|
&mut self,
|
|
operate: &mut dyn FnMut(&mut dyn Operation<S::Output>),
|
|
) {
|
|
if self.strategy.is_done() {
|
|
return;
|
|
}
|
|
|
|
self.stack.push((self.viewport, self.translation));
|
|
operate(self);
|
|
let _ = self.stack.pop();
|
|
|
|
let (viewport, translation) = self.stack.last().unwrap();
|
|
self.viewport = *viewport;
|
|
self.translation = *translation;
|
|
}
|
|
|
|
fn container(&mut self, id: Option<&Id>, bounds: Rectangle) {
|
|
if self.strategy.is_done() {
|
|
return;
|
|
}
|
|
|
|
self.strategy.feed(Candidate::Container {
|
|
id,
|
|
bounds,
|
|
visible_bounds: self
|
|
.viewport
|
|
.intersection(&(bounds + self.translation)),
|
|
});
|
|
}
|
|
|
|
fn focusable(
|
|
&mut self,
|
|
id: Option<&Id>,
|
|
bounds: Rectangle,
|
|
state: &mut dyn Focusable,
|
|
) {
|
|
if self.strategy.is_done() {
|
|
return;
|
|
}
|
|
|
|
self.strategy.feed(Candidate::Focusable {
|
|
id,
|
|
bounds,
|
|
visible_bounds: self
|
|
.viewport
|
|
.intersection(&(bounds + self.translation)),
|
|
state,
|
|
});
|
|
}
|
|
|
|
fn scrollable(
|
|
&mut self,
|
|
id: Option<&Id>,
|
|
bounds: Rectangle,
|
|
content_bounds: Rectangle,
|
|
translation: Vector,
|
|
state: &mut dyn Scrollable,
|
|
) {
|
|
if self.strategy.is_done() {
|
|
return;
|
|
}
|
|
|
|
let visible_bounds =
|
|
self.viewport.intersection(&(bounds + self.translation));
|
|
|
|
self.strategy.feed(Candidate::Scrollable {
|
|
id,
|
|
bounds,
|
|
visible_bounds,
|
|
content_bounds,
|
|
translation,
|
|
state,
|
|
});
|
|
|
|
self.translation = self.translation - translation;
|
|
self.viewport = visible_bounds.unwrap_or_default();
|
|
}
|
|
|
|
fn text_input(
|
|
&mut self,
|
|
id: Option<&Id>,
|
|
bounds: Rectangle,
|
|
state: &mut dyn TextInput,
|
|
) {
|
|
if self.strategy.is_done() {
|
|
return;
|
|
}
|
|
|
|
self.strategy.feed(Candidate::TextInput {
|
|
id,
|
|
bounds,
|
|
visible_bounds: self
|
|
.viewport
|
|
.intersection(&(bounds + self.translation)),
|
|
state,
|
|
});
|
|
}
|
|
|
|
fn text(&mut self, id: Option<&Id>, bounds: Rectangle, text: &str) {
|
|
if self.strategy.is_done() {
|
|
return;
|
|
}
|
|
|
|
self.strategy.feed(Candidate::Text {
|
|
id,
|
|
bounds,
|
|
visible_bounds: self
|
|
.viewport
|
|
.intersection(&(bounds + self.translation)),
|
|
content: text,
|
|
});
|
|
}
|
|
|
|
fn custom(
|
|
&mut self,
|
|
id: Option<&Id>,
|
|
bounds: Rectangle,
|
|
state: &mut dyn Any,
|
|
) {
|
|
if self.strategy.is_done() {
|
|
return;
|
|
}
|
|
|
|
self.strategy.feed(Candidate::Custom {
|
|
id,
|
|
bounds,
|
|
visible_bounds: self
|
|
.viewport
|
|
.intersection(&(bounds + self.translation)),
|
|
state,
|
|
});
|
|
}
|
|
|
|
fn finish(&self) -> Outcome<S::Output> {
|
|
Outcome::Some(self.strategy.finish())
|
|
}
|
|
}
|