800 lines
21 KiB
Rust
800 lines
21 KiB
Rust
mod align;
|
|
mod child_view;
|
|
mod clipped;
|
|
mod clipped_scrollable;
|
|
mod constrained_box;
|
|
mod container;
|
|
#[cfg(debug_assertions)]
|
|
mod debug;
|
|
mod dismiss;
|
|
mod drag;
|
|
pub mod drag_resize;
|
|
mod empty;
|
|
mod event_handler;
|
|
mod flex;
|
|
mod formatted_text_element;
|
|
mod hoverable;
|
|
mod icon;
|
|
mod image;
|
|
mod list;
|
|
mod min_size;
|
|
pub mod new_scrollable;
|
|
mod percentage;
|
|
mod rect;
|
|
pub mod resizable;
|
|
mod scrollable;
|
|
mod selectable_area;
|
|
pub mod shared_scrollbar;
|
|
pub mod shimmering_text;
|
|
mod size_constraint_switch;
|
|
mod stack;
|
|
pub mod table;
|
|
mod text;
|
|
mod uniform_list;
|
|
mod viewported_list;
|
|
|
|
pub use align::*;
|
|
pub use child_view::*;
|
|
pub use clipped::*;
|
|
pub use clipped_scrollable::*;
|
|
pub use constrained_box::*;
|
|
pub use container::*;
|
|
#[cfg(debug_assertions)]
|
|
pub use debug::*;
|
|
pub use dismiss::*;
|
|
pub use drag::*;
|
|
pub use drag_resize::*;
|
|
pub use empty::*;
|
|
pub use event_handler::*;
|
|
pub use flex::*;
|
|
pub use formatted_text_element::*;
|
|
pub use hoverable::*;
|
|
pub use icon::*;
|
|
pub use image::*;
|
|
pub use list::*;
|
|
pub use min_size::*;
|
|
pub use new_scrollable::NewScrollable;
|
|
pub use percentage::*;
|
|
pub use rect::*;
|
|
pub use resizable::*;
|
|
pub use scrollable::*;
|
|
pub use selectable_area::*;
|
|
pub use shared_scrollbar::*;
|
|
pub use size_constraint_switch::*;
|
|
pub use stack::*;
|
|
pub use table::{
|
|
RowBackground, Table, TableColumnWidth, TableConfig, TableHeader, TableState, TableStateHandle,
|
|
TableVerticalSizing,
|
|
};
|
|
pub use text::*;
|
|
pub use uniform_list::*;
|
|
pub use viewported_list::*;
|
|
|
|
use crate::event::ModifiersState;
|
|
use crate::platform::Cursor;
|
|
use crate::{
|
|
event::DispatchedEvent,
|
|
text::{word_boundaries::WordBoundariesPolicy, IsRect, SelectionDirection, SelectionType},
|
|
Gradient,
|
|
};
|
|
pub use crate::{
|
|
scene::Dash, scene::ZIndex, AfterLayoutContext, AppContext, Event, EventContext, LayoutContext,
|
|
PaintContext, SizeConstraint,
|
|
};
|
|
use core::fmt;
|
|
use pathfinder_color::ColorU;
|
|
use pathfinder_geometry::{
|
|
rect::RectF,
|
|
vector::{vec2f, Vector2F},
|
|
};
|
|
use std::any::Any;
|
|
use std::borrow::Cow;
|
|
use std::ops::Range;
|
|
use std::sync::MutexGuard;
|
|
|
|
/// The result of dispatching an event.
|
|
/// This is (future) return type of `dispatch_event`.
|
|
/// This will eventually replace the current boolean return type, to be more explicit about
|
|
/// which events should continue to propagate to parent elements and which should stop.
|
|
pub enum DispatchEventResult {
|
|
/// The event should continue to propagate to parent elements.
|
|
PropagateToParent,
|
|
/// The event should not propagate to parent elements.
|
|
StopPropagation,
|
|
}
|
|
|
|
pub trait Element {
|
|
fn layout(
|
|
&mut self,
|
|
constraint: SizeConstraint,
|
|
ctx: &mut LayoutContext,
|
|
app: &AppContext,
|
|
) -> Vector2F;
|
|
|
|
fn after_layout(&mut self, _: &mut AfterLayoutContext, _: &AppContext);
|
|
|
|
fn paint(&mut self, origin: Vector2F, ctx: &mut PaintContext, app: &AppContext);
|
|
|
|
fn size(&self) -> Option<Vector2F>;
|
|
|
|
fn origin(&self) -> Option<Point>;
|
|
|
|
fn z_index(&self) -> Option<ZIndex> {
|
|
self.origin().map(|p| p.z_index())
|
|
}
|
|
|
|
fn bounds(&self) -> Option<RectF> {
|
|
try_rect_with_z(self.origin(), self.size())
|
|
}
|
|
|
|
fn parent_data(&self) -> Option<&dyn Any> {
|
|
None
|
|
}
|
|
|
|
/// Should be implemented alongside the SelectableElement trait. If implemented, it
|
|
/// should return the element as a SelectableElement.
|
|
fn as_selectable_element(&self) -> Option<&dyn SelectableElement> {
|
|
None
|
|
}
|
|
|
|
/// Handle an event from the OS (e.g. Mouse or Keyboard events)
|
|
///
|
|
/// Note: For each OS event, this is called on the root Element of the Element tree. Each
|
|
/// Element is then itself responsible for calling `dispatch_event` on its children. The
|
|
/// expectations for how an event propagates through the Element tree are:
|
|
///
|
|
/// 1. Each Element that handles an event in some meaningful way will first verify that the
|
|
/// event applies to them by doing any necessary hit testing.
|
|
/// 2. Each parent Element will unconditionally pass the event to its children by calling
|
|
/// `dispatch_event` on them, which allows the children to make their own determination
|
|
/// of whether or not the event applies.
|
|
/// 3. Elements should return true if they handled the event and don't want it to propagate
|
|
/// to parent elements, and false if they want it to propagate to parent elements.
|
|
fn dispatch_event(
|
|
&mut self,
|
|
event: &DispatchedEvent,
|
|
ctx: &mut EventContext,
|
|
app: &AppContext,
|
|
) -> bool;
|
|
|
|
fn finish(self) -> Box<dyn Element>
|
|
where
|
|
Self: 'static + Sized,
|
|
{
|
|
Box::new(self)
|
|
}
|
|
|
|
#[cfg(debug_assertions)]
|
|
fn type_name(&self) -> &'static str {
|
|
std::any::type_name::<Self>()
|
|
}
|
|
|
|
/// Returns the text content of this element, if it contains text.
|
|
/// This is primarily used for testing to verify rendered text content.
|
|
/// Container elements should aggregate text from their children.
|
|
#[cfg(any(test, feature = "test-util"))]
|
|
fn debug_text_content(&self) -> Option<String> {
|
|
None
|
|
}
|
|
}
|
|
|
|
pub trait ParentElement: Extend<Box<dyn Element>> + Sized {
|
|
#[cfg_attr(debug_assertions, track_caller)]
|
|
fn add_children(&mut self, children: impl IntoIterator<Item = Box<dyn Element>>) {
|
|
self.extend(children);
|
|
}
|
|
|
|
#[cfg_attr(debug_assertions, track_caller)]
|
|
fn add_child(&mut self, child: Box<dyn Element>) {
|
|
self.extend(Some(child))
|
|
}
|
|
|
|
#[cfg_attr(debug_assertions, track_caller)]
|
|
fn with_children(mut self, children: impl IntoIterator<Item = Box<dyn Element>>) -> Self {
|
|
self.add_children(children);
|
|
self
|
|
}
|
|
|
|
#[cfg_attr(debug_assertions, track_caller)]
|
|
fn with_child(self, child: Box<dyn Element>) -> Self {
|
|
self.with_children(Some(child))
|
|
}
|
|
}
|
|
|
|
impl<T> ParentElement for T where T: Extend<Box<dyn Element>> {}
|
|
|
|
#[derive(Clone, Debug)]
|
|
pub struct SelectionFragment {
|
|
pub text: String,
|
|
pub origin: Point,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
|
pub struct Point {
|
|
xy: Vector2F,
|
|
z_index: ZIndex,
|
|
}
|
|
|
|
impl Point {
|
|
pub fn new(x: f32, y: f32, z_index: ZIndex) -> Self {
|
|
Self {
|
|
xy: vec2f(x, y),
|
|
z_index,
|
|
}
|
|
}
|
|
|
|
pub fn from_vec2f(xy: Vector2F, z_index: ZIndex) -> Self {
|
|
Self { xy, z_index }
|
|
}
|
|
|
|
pub fn x(&self) -> f32 {
|
|
self.xy.x()
|
|
}
|
|
|
|
pub fn y(&self) -> f32 {
|
|
self.xy.y()
|
|
}
|
|
|
|
pub fn xy(&self) -> Vector2F {
|
|
self.xy
|
|
}
|
|
|
|
pub fn z_index(&self) -> ZIndex {
|
|
self.z_index
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
|
pub enum Axis {
|
|
Horizontal,
|
|
Vertical,
|
|
}
|
|
|
|
impl Axis {
|
|
pub fn invert(self) -> Self {
|
|
match self {
|
|
Self::Horizontal => Self::Vertical,
|
|
Self::Vertical => Self::Horizontal,
|
|
}
|
|
}
|
|
|
|
pub fn to_point(self, pos_along_main_axis: f32, pos_along_inverse_axis: f32) -> Vector2F {
|
|
match self {
|
|
Self::Horizontal => vec2f(pos_along_main_axis, pos_along_inverse_axis),
|
|
Self::Vertical => vec2f(pos_along_inverse_axis, pos_along_main_axis),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub enum AxisOrientation {
|
|
Normal,
|
|
Reverse,
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug, Default, PartialEq)]
|
|
pub enum Fill {
|
|
#[default]
|
|
None,
|
|
Solid(ColorU),
|
|
Gradient {
|
|
start: Vector2F,
|
|
end: Vector2F,
|
|
start_color: ColorU,
|
|
end_color: ColorU,
|
|
},
|
|
}
|
|
|
|
impl From<ColorU> for Fill {
|
|
fn from(color: ColorU) -> Self {
|
|
Fill::Solid(color)
|
|
}
|
|
}
|
|
|
|
#[derive(Default, Debug, Clone, Copy, PartialEq)]
|
|
pub struct Margin {
|
|
top: f32,
|
|
left: f32,
|
|
bottom: f32,
|
|
right: f32,
|
|
}
|
|
|
|
impl Margin {
|
|
pub const fn uniform(margin: f32) -> Self {
|
|
Margin {
|
|
top: margin,
|
|
left: margin,
|
|
bottom: margin,
|
|
right: margin,
|
|
}
|
|
}
|
|
|
|
pub const fn with_left(mut self, margin: f32) -> Self {
|
|
self.left = margin;
|
|
self
|
|
}
|
|
|
|
pub const fn with_right(mut self, margin: f32) -> Self {
|
|
self.right = margin;
|
|
self
|
|
}
|
|
|
|
pub const fn with_top(mut self, margin: f32) -> Self {
|
|
self.top = margin;
|
|
self
|
|
}
|
|
|
|
pub const fn with_bottom(mut self, margin: f32) -> Self {
|
|
self.bottom = margin;
|
|
self
|
|
}
|
|
|
|
pub fn top(&self) -> f32 {
|
|
self.top
|
|
}
|
|
|
|
pub fn left(&self) -> f32 {
|
|
self.left
|
|
}
|
|
|
|
pub fn bottom(&self) -> f32 {
|
|
self.bottom
|
|
}
|
|
|
|
pub fn right(&self) -> f32 {
|
|
self.right
|
|
}
|
|
}
|
|
|
|
#[derive(Default, Debug, Clone, Copy, PartialEq)]
|
|
pub struct Padding {
|
|
top: f32,
|
|
left: f32,
|
|
bottom: f32,
|
|
right: f32,
|
|
}
|
|
|
|
impl Padding {
|
|
pub const fn uniform(padding: f32) -> Self {
|
|
Self {
|
|
top: padding,
|
|
left: padding,
|
|
bottom: padding,
|
|
right: padding,
|
|
}
|
|
}
|
|
|
|
pub const fn with_top(mut self, padding: f32) -> Self {
|
|
self.top = padding;
|
|
self
|
|
}
|
|
|
|
pub const fn with_left(mut self, padding: f32) -> Self {
|
|
self.left = padding;
|
|
self
|
|
}
|
|
|
|
pub const fn with_bottom(mut self, padding: f32) -> Self {
|
|
self.bottom = padding;
|
|
self
|
|
}
|
|
|
|
pub const fn with_right(mut self, padding: f32) -> Self {
|
|
self.right = padding;
|
|
self
|
|
}
|
|
|
|
pub fn with_vertical(mut self, vertical: f32) -> Self {
|
|
self.top = vertical;
|
|
self.bottom = vertical;
|
|
self
|
|
}
|
|
|
|
pub fn with_horizontal(mut self, horizontal: f32) -> Self {
|
|
self.left = horizontal;
|
|
self.right = horizontal;
|
|
self
|
|
}
|
|
|
|
pub fn top(&self) -> f32 {
|
|
self.top
|
|
}
|
|
|
|
pub fn left(&self) -> f32 {
|
|
self.left
|
|
}
|
|
|
|
pub fn bottom(&self) -> f32 {
|
|
self.bottom
|
|
}
|
|
|
|
pub fn right(&self) -> f32 {
|
|
self.right
|
|
}
|
|
}
|
|
|
|
#[derive(Default)]
|
|
pub struct Overdraw {
|
|
top: f32,
|
|
left: f32,
|
|
bottom: f32,
|
|
right: f32,
|
|
}
|
|
|
|
impl Border {
|
|
pub const fn new(width: f32) -> Self {
|
|
Self {
|
|
width,
|
|
color: Fill::None,
|
|
top: false,
|
|
left: false,
|
|
bottom: false,
|
|
right: false,
|
|
dash: None,
|
|
}
|
|
}
|
|
|
|
pub fn all(width: f32) -> Self {
|
|
Self {
|
|
width,
|
|
color: Fill::None,
|
|
top: true,
|
|
left: true,
|
|
bottom: true,
|
|
right: true,
|
|
dash: None,
|
|
}
|
|
}
|
|
|
|
pub fn top(width: f32) -> Self {
|
|
let mut border = Self::new(width);
|
|
border.top = true;
|
|
border
|
|
}
|
|
|
|
pub fn left(width: f32) -> Self {
|
|
let mut border = Self::new(width);
|
|
border.left = true;
|
|
border
|
|
}
|
|
|
|
pub fn bottom(width: f32) -> Self {
|
|
let mut border = Self::new(width);
|
|
border.bottom = true;
|
|
border
|
|
}
|
|
|
|
pub fn right(width: f32) -> Self {
|
|
let mut border = Self::new(width);
|
|
border.right = true;
|
|
border
|
|
}
|
|
|
|
pub fn with_sides(mut self, top: bool, left: bool, bottom: bool, right: bool) -> Self {
|
|
self.top = top;
|
|
self.left = left;
|
|
self.bottom = bottom;
|
|
self.right = right;
|
|
self
|
|
}
|
|
|
|
pub fn with_border_fill<F>(mut self, fill: F) -> Self
|
|
where
|
|
F: Into<Fill>,
|
|
{
|
|
self.color = fill.into();
|
|
self
|
|
}
|
|
|
|
pub fn with_border_color(mut self, color: ColorU) -> Self {
|
|
self.color = Fill::Solid(color);
|
|
self
|
|
}
|
|
|
|
pub fn with_horizontal_border_gradient(mut self, gradient: Gradient) -> Self {
|
|
self.color = Fill::Gradient {
|
|
start: vec2f(0.0, 0.0),
|
|
end: vec2f(1.0, 0.0),
|
|
start_color: gradient.start,
|
|
end_color: gradient.end,
|
|
};
|
|
self
|
|
}
|
|
|
|
pub fn with_border_gradient(
|
|
mut self,
|
|
start: Vector2F,
|
|
end: Vector2F,
|
|
gradient: Gradient,
|
|
) -> Self {
|
|
self.color = Fill::Gradient {
|
|
start,
|
|
end,
|
|
start_color: gradient.start,
|
|
end_color: gradient.end,
|
|
};
|
|
self
|
|
}
|
|
|
|
/// Note: only implemented for sharp corners. ***DO NOT*** use for elements with corner radius != 0, as this causes visual bugs.
|
|
pub fn with_dashed_border(mut self, dash: Dash) -> Self {
|
|
self.dash = Some(dash);
|
|
self
|
|
}
|
|
}
|
|
|
|
impl From<ColorU> for Border {
|
|
fn from(value: ColorU) -> Self {
|
|
Border::all(1.).with_border_color(value)
|
|
}
|
|
}
|
|
|
|
impl Fill {
|
|
pub fn start(&self) -> Vector2F {
|
|
match self {
|
|
Self::Gradient { start, .. } => *start,
|
|
_ => vec2f(0.0, 0.0),
|
|
}
|
|
}
|
|
|
|
pub fn end(&self) -> Vector2F {
|
|
match self {
|
|
Self::Gradient { end, .. } => *end,
|
|
_ => vec2f(1.0, 0.0),
|
|
}
|
|
}
|
|
|
|
pub fn start_color(&self) -> ColorU {
|
|
match self {
|
|
Self::Gradient { start_color, .. } => *start_color,
|
|
Self::Solid(color) => *color,
|
|
Self::None => ColorU::transparent_black(),
|
|
}
|
|
}
|
|
|
|
pub fn end_color(&self) -> ColorU {
|
|
match self {
|
|
Self::Gradient { end_color, .. } => *end_color,
|
|
Self::Solid(color) => *color,
|
|
Self::None => ColorU::transparent_black(),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Extends the `Vector2F` API to provider richer APIs for
|
|
/// element-related computations.
|
|
pub trait Vector2FExt {
|
|
/// Converts the 2D vector to a scalar according to the given `axis`.
|
|
fn along(self, axis: Axis) -> f32;
|
|
|
|
/// Projects the 2D vector onto the given `axis`.
|
|
/// e.g. (5, 2) -> (5, 0), along the x-axis.
|
|
fn project_onto(self, axis: Axis) -> Vector2F;
|
|
|
|
/// [`fmt::Display`] impl to format this `Vector2F` as a point.
|
|
fn display_point(self) -> Vector2FDisplayPoint;
|
|
|
|
/// [`fmt::Display`] impl to format this `Vector2F` as a size.
|
|
fn display_size(self) -> Vector2FDisplaySize;
|
|
}
|
|
|
|
impl Vector2FExt for Vector2F {
|
|
fn along(self, axis: Axis) -> f32 {
|
|
match axis {
|
|
Axis::Horizontal => self.x(),
|
|
Axis::Vertical => self.y(),
|
|
}
|
|
}
|
|
|
|
fn project_onto(self, axis: Axis) -> Vector2F {
|
|
match axis {
|
|
Axis::Horizontal => vec2f(self.x(), 0.),
|
|
Axis::Vertical => vec2f(0., self.y()),
|
|
}
|
|
}
|
|
|
|
fn display_point(self) -> Vector2FDisplayPoint {
|
|
Vector2FDisplayPoint(self)
|
|
}
|
|
|
|
fn display_size(self) -> Vector2FDisplaySize {
|
|
Vector2FDisplaySize(self)
|
|
}
|
|
}
|
|
|
|
pub struct Vector2FDisplaySize(Vector2F);
|
|
|
|
impl fmt::Display for Vector2FDisplaySize {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
// We have to call .fmt directly so that formatting options are propagated.
|
|
self.0.x().fmt(f)?;
|
|
f.write_str("x")?;
|
|
self.0.y().fmt(f)
|
|
}
|
|
}
|
|
|
|
pub struct Vector2FDisplayPoint(Vector2F);
|
|
|
|
impl fmt::Display for Vector2FDisplayPoint {
|
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
|
// We have to call .fmt directly so that formatting options are propagated.
|
|
f.write_str("(")?;
|
|
self.0.x().fmt(f)?;
|
|
f.write_str(", ")?;
|
|
self.0.y().fmt(f)?;
|
|
f.write_str(")")
|
|
}
|
|
}
|
|
|
|
/// Extends the `f32` API to provider richer APIs for
|
|
/// element-related computations.
|
|
pub trait F32Ext {
|
|
/// Converts the `f32` to a 2D vector along the provided `axis`.
|
|
fn along(self, axis: Axis) -> Vector2F;
|
|
}
|
|
|
|
impl F32Ext for f32 {
|
|
fn along(self, axis: Axis) -> Vector2F {
|
|
match axis {
|
|
Axis::Horizontal => vec2f(self, 0.),
|
|
Axis::Vertical => vec2f(0., self),
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Extends the `RectF` API to provider richer APIs for
|
|
/// element-related computations.
|
|
pub trait RectFExt {
|
|
/// Returns the minimum value along the given `axis`.
|
|
fn min_along(self, axis: Axis) -> f32;
|
|
|
|
/// Returns the maximum value along the given `axis`.
|
|
fn max_along(self, axis: Axis) -> f32;
|
|
}
|
|
|
|
impl RectFExt for RectF {
|
|
fn min_along(self, axis: Axis) -> f32 {
|
|
match axis {
|
|
Axis::Horizontal => self.min_x(),
|
|
Axis::Vertical => self.min_y(),
|
|
}
|
|
}
|
|
|
|
fn max_along(self, axis: Axis) -> f32 {
|
|
match axis {
|
|
Axis::Horizontal => self.max_x(),
|
|
Axis::Vertical => self.max_y(),
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn try_rect(origin: Option<Vector2F>, size: Option<Vector2F>) -> Option<RectF> {
|
|
origin.and_then(|origin| size.map(|size| RectF::new(origin, size)))
|
|
}
|
|
|
|
pub fn try_rect_with_z(origin: Option<Point>, size: Option<Vector2F>) -> Option<RectF> {
|
|
origin.and_then(|origin| size.map(|size| RectF::new(origin.xy(), size)))
|
|
}
|
|
|
|
/// The click handler provides the caller with the clicked text chunk index in
|
|
/// the provided clickable char ranges and the string corresponds to that chunk,
|
|
/// if one of the clickable chunks were clicked
|
|
pub type ClickHandler = Box<dyn FnMut(&ModifiersState, &mut EventContext, &AppContext)>;
|
|
/// The hover handler is called when the mouse either hovers or unhovers over a
|
|
/// hoverable char range, with the first argument being is_hovering.
|
|
pub type HoverHandler = Box<dyn FnMut(bool, &mut EventContext, &AppContext)>;
|
|
|
|
pub(crate) struct ClickableCharRange {
|
|
pub(crate) char_range: Range<usize>,
|
|
pub(crate) click_handler: ClickHandler,
|
|
}
|
|
|
|
pub(crate) struct HoverableCharRange {
|
|
pub(crate) char_range: Range<usize>,
|
|
pub(crate) hover_handler: HoverHandler,
|
|
pub(crate) cursor_on_hover: Option<Cursor>,
|
|
pub(crate) mouse_state: MouseStateHandle,
|
|
}
|
|
|
|
impl HoverableCharRange {
|
|
fn mouse_state(&self) -> MutexGuard<'_, MouseState> {
|
|
self.mouse_state
|
|
.lock()
|
|
.expect("The hoverable range should lock mouse state")
|
|
}
|
|
}
|
|
|
|
/// SecretRange is used to store both the char range and byte range of a secret.
|
|
/// We need to do this since several APIs e.g. hover/click APIs, use char ranges,
|
|
/// whereas text-related APIs e.g. Regex and replace_range, use byte ranges.
|
|
#[derive(Debug, Eq, PartialEq, Hash, Clone)]
|
|
pub struct SecretRange {
|
|
pub char_range: Range<usize>,
|
|
pub byte_range: Range<usize>,
|
|
}
|
|
|
|
impl SecretRange {
|
|
/// Extends the current range to include the provided range.
|
|
pub fn extend_range_end(&mut self, other: &SecretRange) {
|
|
self.char_range.end = self.char_range.end.max(other.char_range.end);
|
|
self.byte_range.end = self.byte_range.end.max(other.byte_range.end);
|
|
}
|
|
}
|
|
|
|
pub trait PartialClickableElement {
|
|
/// clickable_char_ranges is the vector of char ranges that the caller can
|
|
/// specify, where the callback will be called if any character in one of
|
|
/// those char ranges was clicked
|
|
fn with_clickable_char_range<F>(
|
|
self,
|
|
_clickable_char_range: Range<usize>,
|
|
_callback: F,
|
|
) -> Self
|
|
where
|
|
F: 'static + FnMut(&ModifiersState, &mut EventContext, &AppContext);
|
|
|
|
/// Registers a callback that is called when a character in the given hoverable_char_range
|
|
/// is hovered or unhovered.
|
|
fn with_hoverable_char_range<F>(
|
|
self,
|
|
hoverable_char_range: Range<usize>,
|
|
mouse_state: MouseStateHandle,
|
|
cursor_on_hover: Option<Cursor>,
|
|
callback: F,
|
|
) -> Self
|
|
where
|
|
F: 'static + FnMut(bool, &mut EventContext, &AppContext);
|
|
|
|
/// Replace in the given range of the text with the replacement text.
|
|
fn replace_text_range(&mut self, range: SecretRange, replacement: Cow<'static, str>);
|
|
}
|
|
|
|
/// An element that can be selected, for use with the SelectableArea element.
|
|
/// It is expected that an element implementing this trait (i.e. Text)
|
|
/// also implements as_selectable_element().
|
|
pub trait SelectableElement {
|
|
/// Return the element's selected fragments.
|
|
fn get_selection(
|
|
&self,
|
|
_selection_start: Vector2F,
|
|
_selection_end: Vector2F,
|
|
_is_rect: IsRect,
|
|
) -> Option<Vec<SelectionFragment>>;
|
|
|
|
/// Semantically expands the absolute selection point based on the unit.
|
|
/// Does nothing if the unit is Char because there is no need to expand.
|
|
/// Expands to the start of the unit if expand_to_start is true, otherwise
|
|
/// expands to the end of the unit.
|
|
/// If the absolute point before the element's bounds and expand_to_start is true,
|
|
/// should expand to the start of the element. Similarly, if the absolute point is after
|
|
/// the element's bounds and expand_to_start is false, should expand to the end of the element.
|
|
/// Otherwise, should return None.
|
|
fn expand_selection(
|
|
&self,
|
|
_absolute_point: Vector2F,
|
|
_direction: SelectionDirection,
|
|
_unit: SelectionType,
|
|
_word_boundaries_policy: &WordBoundariesPolicy,
|
|
) -> Option<Vector2F>;
|
|
|
|
/// Returns None if neither point is in the element.
|
|
fn is_point_semantically_before(
|
|
&self,
|
|
_absolute_point: Vector2F,
|
|
_absolute_point_other: Vector2F,
|
|
) -> Option<bool>;
|
|
|
|
/// Runs smart selection on a point.
|
|
/// Should return None if the point is outside the element vertically,
|
|
/// but should snap to the nearest line if out of bounds horizontally.
|
|
fn smart_select(
|
|
&self,
|
|
_absolute_point: Vector2F,
|
|
_smart_select_fn: SmartSelectFn,
|
|
) -> Option<(Vector2F, Vector2F)>;
|
|
|
|
/// The union of the returned regions defines the area within which a mouse click is considered
|
|
/// to be a click performed on the element's selection. Should return an empty vector for
|
|
/// elements that don't define any selection-specific click behaviors.
|
|
fn calculate_clickable_bounds(&self, _current_selection: Option<Selection>) -> Vec<RectF>;
|
|
}
|