Merge branch 'main' into divs
This commit is contained in:
commit
d375f7992d
277 changed files with 19044 additions and 8896 deletions
|
@ -574,6 +574,14 @@ impl AppContext {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn optional_global<T: 'static>(&self) -> Option<&T> {
|
||||
if let Some(global) = self.globals.get(&TypeId::of::<T>()) {
|
||||
Some(global.downcast_ref().unwrap())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn upgrade(&self) -> App {
|
||||
App(self.weak_self.as_ref().unwrap().upgrade().unwrap())
|
||||
}
|
||||
|
@ -3284,7 +3292,11 @@ impl<'a, 'b, V: 'static> ViewContext<'a, 'b, V> {
|
|||
}
|
||||
|
||||
pub fn mouse_state<Tag: 'static>(&self, region_id: usize) -> MouseState {
|
||||
let region_id = MouseRegionId::new::<Tag>(self.view_id, region_id);
|
||||
self.mouse_state_dynamic(TypeTag::new::<Tag>(), region_id)
|
||||
}
|
||||
|
||||
pub fn mouse_state_dynamic(&self, tag: TypeTag, region_id: usize) -> MouseState {
|
||||
let region_id = MouseRegionId::new(tag, self.view_id, region_id);
|
||||
MouseState {
|
||||
hovered: self.window.hovered_region_ids.contains(®ion_id),
|
||||
clicked: if let Some((clicked_region_id, button)) = self.window.clicked_region {
|
||||
|
@ -3305,11 +3317,20 @@ impl<'a, 'b, V: 'static> ViewContext<'a, 'b, V> {
|
|||
&mut self,
|
||||
element_id: usize,
|
||||
initial: T,
|
||||
) -> ElementStateHandle<T> {
|
||||
self.element_state_dynamic(TypeTag::new::<Tag>(), element_id, initial)
|
||||
}
|
||||
|
||||
pub fn element_state_dynamic<T: 'static>(
|
||||
&mut self,
|
||||
tag: TypeTag,
|
||||
element_id: usize,
|
||||
initial: T,
|
||||
) -> ElementStateHandle<T> {
|
||||
let id = ElementStateId {
|
||||
view_id: self.view_id(),
|
||||
element_id,
|
||||
tag: TypeId::of::<Tag>(),
|
||||
tag,
|
||||
};
|
||||
self.element_states
|
||||
.entry(id)
|
||||
|
@ -3327,19 +3348,65 @@ impl<'a, 'b, V: 'static> ViewContext<'a, 'b, V> {
|
|||
pub fn rem_pixels(&self) -> f32 {
|
||||
16.
|
||||
}
|
||||
|
||||
pub fn default_element_state_dynamic<T: 'static + Default>(
|
||||
&mut self,
|
||||
tag: TypeTag,
|
||||
element_id: usize,
|
||||
) -> ElementStateHandle<T> {
|
||||
self.element_state_dynamic::<T>(tag, element_id, T::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> ViewContext<'_, '_, V> {
|
||||
pub fn emit(&mut self, payload: V::Event) {
|
||||
pub fn emit(&mut self, event: V::Event) {
|
||||
self.window_context
|
||||
.pending_effects
|
||||
.push_back(Effect::Event {
|
||||
entity_id: self.view_id,
|
||||
payload: Box::new(payload),
|
||||
payload: Box::new(event),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
|
||||
pub struct TypeTag {
|
||||
tag: TypeId,
|
||||
composed: Option<TypeId>,
|
||||
#[cfg(debug_assertions)]
|
||||
tag_type_name: &'static str,
|
||||
}
|
||||
|
||||
impl TypeTag {
|
||||
pub fn new<Tag: 'static>() -> Self {
|
||||
Self {
|
||||
tag: TypeId::of::<Tag>(),
|
||||
composed: None,
|
||||
#[cfg(debug_assertions)]
|
||||
tag_type_name: std::any::type_name::<Tag>(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dynamic(tag: TypeId, #[cfg(debug_assertions)] type_name: &'static str) -> Self {
|
||||
Self {
|
||||
tag,
|
||||
composed: None,
|
||||
#[cfg(debug_assertions)]
|
||||
tag_type_name: type_name,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn compose(mut self, other: TypeTag) -> Self {
|
||||
self.composed = Some(other.tag);
|
||||
self
|
||||
}
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
pub(crate) fn type_name(&self) -> &'static str {
|
||||
self.tag_type_name
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> BorrowAppContext for ViewContext<'_, '_, V> {
|
||||
fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
|
||||
BorrowAppContext::read_with(&*self.window_context, f)
|
||||
|
@ -4789,7 +4856,7 @@ impl Hash for AnyWeakViewHandle {
|
|||
pub struct ElementStateId {
|
||||
view_id: usize,
|
||||
element_id: usize,
|
||||
tag: TypeId,
|
||||
tag: TypeTag,
|
||||
}
|
||||
|
||||
pub struct ElementStateHandle<T> {
|
||||
|
@ -5251,7 +5318,7 @@ mod tests {
|
|||
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||
enum Handler {}
|
||||
let mouse_down_count = self.mouse_down_count.clone();
|
||||
MouseEventHandler::<Handler, _>::new(0, cx, |_, _| Empty::new())
|
||||
MouseEventHandler::new::<Handler, _>(0, cx, |_, _| Empty::new())
|
||||
.on_down(MouseButton::Left, move |_, _, _| {
|
||||
mouse_down_count.fetch_add(1, SeqCst);
|
||||
})
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
use std::any::{Any, TypeId};
|
||||
|
||||
use crate::TypeTag;
|
||||
|
||||
pub trait Action: 'static {
|
||||
fn id(&self) -> TypeId;
|
||||
fn namespace(&self) -> &'static str;
|
||||
fn name(&self) -> &'static str;
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn type_tag(&self) -> TypeTag;
|
||||
fn boxed_clone(&self) -> Box<dyn Action>;
|
||||
fn eq(&self, other: &dyn Action) -> bool;
|
||||
|
||||
|
@ -107,6 +110,10 @@ macro_rules! __impl_action {
|
|||
}
|
||||
}
|
||||
|
||||
fn type_tag(&self) -> $crate::TypeTag {
|
||||
$crate::TypeTag::new::<Self>()
|
||||
}
|
||||
|
||||
$from_json_fn
|
||||
}
|
||||
};
|
||||
|
|
|
@ -507,7 +507,7 @@ impl<'a> WindowContext<'a> {
|
|||
}
|
||||
|
||||
pub(crate) fn dispatch_event(&mut self, event: Event, event_reused: bool) -> bool {
|
||||
self.dispatch_to_interactive_regions(&event);
|
||||
self.dispatch_to_new_event_handlers(&event);
|
||||
|
||||
let mut mouse_events = SmallVec::<[_; 2]>::new();
|
||||
let mut notified_views: HashSet<usize> = Default::default();
|
||||
|
@ -886,9 +886,8 @@ impl<'a> WindowContext<'a> {
|
|||
any_event_handled
|
||||
}
|
||||
|
||||
fn dispatch_to_interactive_regions(&mut self, event: &Event) {
|
||||
fn dispatch_to_new_event_handlers(&mut self, event: &Event) {
|
||||
if let Some(mouse_event) = event.mouse_event() {
|
||||
let mouse_position = event.position().expect("mouse events must have a position");
|
||||
let event_handlers = self.window.take_event_handlers();
|
||||
for event_handler in event_handlers.iter().rev() {
|
||||
if event_handler.event_type == mouse_event.type_id() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
mod align;
|
||||
mod canvas;
|
||||
mod clipped;
|
||||
mod component;
|
||||
mod constrained_box;
|
||||
mod container;
|
||||
mod empty;
|
||||
|
@ -21,9 +22,9 @@ mod tooltip;
|
|||
mod uniform_list;
|
||||
|
||||
pub use self::{
|
||||
align::*, canvas::*, constrained_box::*, container::*, empty::*, flex::*, hook::*, image::*,
|
||||
keystroke_label::*, label::*, list::*, mouse_event_handler::*, overlay::*, resizable::*,
|
||||
stack::*, svg::*, text::*, tooltip::*, uniform_list::*,
|
||||
align::*, canvas::*, component::*, constrained_box::*, container::*, empty::*, flex::*,
|
||||
hook::*, image::*, keystroke_label::*, label::*, list::*, mouse_event_handler::*, overlay::*,
|
||||
resizable::*, stack::*, svg::*, text::*, tooltip::*, uniform_list::*,
|
||||
};
|
||||
pub use crate::window::ChildView;
|
||||
|
||||
|
@ -33,7 +34,7 @@ use crate::{
|
|||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json, Action, Entity, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
json, Action, Entity, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, TypeTag, View,
|
||||
ViewContext, WeakViewHandle, WindowContext,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
|
@ -41,12 +42,21 @@ use collections::HashMap;
|
|||
use core::panic;
|
||||
use json::ToJson;
|
||||
use smallvec::SmallVec;
|
||||
use std::{any::Any, borrow::Cow, mem, ops::Range};
|
||||
use std::{
|
||||
any::{type_name, Any},
|
||||
borrow::Cow,
|
||||
mem,
|
||||
ops::Range,
|
||||
};
|
||||
|
||||
pub trait Element<V: 'static>: 'static {
|
||||
type LayoutState;
|
||||
type PaintState;
|
||||
|
||||
fn view_name(&self) -> &'static str {
|
||||
type_name::<V>()
|
||||
}
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
|
@ -167,6 +177,20 @@ pub trait Element<V: 'static>: 'static {
|
|||
FlexItem::new(self.into_any()).float()
|
||||
}
|
||||
|
||||
fn with_dynamic_tooltip(
|
||||
self,
|
||||
tag: TypeTag,
|
||||
id: usize,
|
||||
text: impl Into<Cow<'static, str>>,
|
||||
action: Option<Box<dyn Action>>,
|
||||
style: TooltipStyle,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Tooltip<V>
|
||||
where
|
||||
Self: 'static + Sized,
|
||||
{
|
||||
Tooltip::new_dynamic(tag, id, text, action, style, self.into_any(), cx)
|
||||
}
|
||||
fn with_tooltip<Tag: 'static>(
|
||||
self,
|
||||
id: usize,
|
||||
|
@ -181,23 +205,34 @@ pub trait Element<V: 'static>: 'static {
|
|||
Tooltip::new::<Tag>(id, text, action, style, self.into_any(), cx)
|
||||
}
|
||||
|
||||
fn resizable(
|
||||
/// Uses the the given element to calculate resizes for the given tag
|
||||
fn provide_resize_bounds<Tag: 'static>(self) -> BoundsProvider<V, Tag>
|
||||
where
|
||||
Self: 'static + Sized,
|
||||
{
|
||||
BoundsProvider::<_, Tag>::new(self.into_any())
|
||||
}
|
||||
|
||||
/// Calls the given closure with the new size of the element whenever the
|
||||
/// handle is dragged. This will be calculated in relation to the bounds
|
||||
/// provided by the given tag
|
||||
fn resizable<Tag: 'static>(
|
||||
self,
|
||||
side: HandleSide,
|
||||
size: f32,
|
||||
on_resize: impl 'static + FnMut(&mut V, f32, &mut ViewContext<V>),
|
||||
on_resize: impl 'static + FnMut(&mut V, Option<f32>, &mut ViewContext<V>),
|
||||
) -> Resizable<V>
|
||||
where
|
||||
Self: 'static + Sized,
|
||||
{
|
||||
Resizable::new(self.into_any(), side, size, on_resize)
|
||||
Resizable::new::<Tag>(self.into_any(), side, size, on_resize)
|
||||
}
|
||||
|
||||
fn mouse<Tag>(self, region_id: usize) -> MouseEventHandler<Tag, V>
|
||||
fn mouse<Tag: 'static>(self, region_id: usize) -> MouseEventHandler<V>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
MouseEventHandler::for_child(self.into_any(), region_id)
|
||||
MouseEventHandler::for_child::<Tag>(self.into_any(), region_id)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -267,8 +302,16 @@ impl<V, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
|
|||
| ElementState::PostLayout { mut element, .. }
|
||||
| ElementState::PostPaint { mut element, .. } => {
|
||||
let (size, layout) = element.layout(constraint, view, cx);
|
||||
debug_assert!(size.x().is_finite());
|
||||
debug_assert!(size.y().is_finite());
|
||||
debug_assert!(
|
||||
size.x().is_finite(),
|
||||
"Element for {:?} had infinite x size after layout",
|
||||
element.view_name()
|
||||
);
|
||||
debug_assert!(
|
||||
size.y().is_finite(),
|
||||
"Element for {:?} had infinite y size after layout",
|
||||
element.view_name()
|
||||
);
|
||||
|
||||
result = size;
|
||||
ElementState::PostLayout {
|
||||
|
|
190
crates/gpui/src/elements/component.rs
Normal file
190
crates/gpui/src/elements/component.rs
Normal file
|
@ -0,0 +1,190 @@
|
|||
use std::marker::PhantomData;
|
||||
|
||||
use pathfinder_geometry::{rect::RectF, vector::Vector2F};
|
||||
|
||||
use crate::{
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, View,
|
||||
ViewContext,
|
||||
};
|
||||
|
||||
use super::Empty;
|
||||
|
||||
pub trait GeneralComponent {
|
||||
fn render<V: View>(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
|
||||
fn element<V: View>(self) -> ComponentAdapter<V, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
ComponentAdapter::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
pub trait StyleableComponent {
|
||||
type Style: Clone;
|
||||
type Output: GeneralComponent;
|
||||
|
||||
fn with_style(self, style: Self::Style) -> Self::Output;
|
||||
}
|
||||
|
||||
impl GeneralComponent for () {
|
||||
fn render<V: View>(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
|
||||
Empty::new().into_any()
|
||||
}
|
||||
}
|
||||
|
||||
impl StyleableComponent for () {
|
||||
type Style = ();
|
||||
type Output = ();
|
||||
|
||||
fn with_style(self, _: Self::Style) -> Self::Output {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
pub trait Component<V: View> {
|
||||
fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
|
||||
|
||||
fn element(self) -> ComponentAdapter<V, Self>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
ComponentAdapter::new(self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View, C: GeneralComponent> Component<V> for C {
|
||||
fn render(self, v: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V> {
|
||||
self.render(v, cx)
|
||||
}
|
||||
}
|
||||
|
||||
// StylableComponent -> GeneralComponent
|
||||
pub struct StylableComponentAdapter<C: Component<V>, V: View> {
|
||||
component: C,
|
||||
phantom: std::marker::PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<C: Component<V>, V: View> StylableComponentAdapter<C, V> {
|
||||
pub fn new(component: C) -> Self {
|
||||
Self {
|
||||
component,
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<C: GeneralComponent, V: View> StyleableComponent for StylableComponentAdapter<C, V> {
|
||||
type Style = ();
|
||||
|
||||
type Output = C;
|
||||
|
||||
fn with_style(self, _: Self::Style) -> Self::Output {
|
||||
self.component
|
||||
}
|
||||
}
|
||||
|
||||
// Element -> Component
|
||||
pub struct ElementAdapter<V: View> {
|
||||
element: AnyElement<V>,
|
||||
_phantom: std::marker::PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<V: View> ElementAdapter<V> {
|
||||
pub fn new(element: AnyElement<V>) -> Self {
|
||||
Self {
|
||||
element,
|
||||
_phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> Component<V> for ElementAdapter<V> {
|
||||
fn render(self, _: &mut V, _: &mut ViewContext<V>) -> AnyElement<V> {
|
||||
self.element
|
||||
}
|
||||
}
|
||||
|
||||
// Component -> Element
|
||||
pub struct ComponentAdapter<V: View, E> {
|
||||
component: Option<E>,
|
||||
element: Option<AnyElement<V>>,
|
||||
phantom: PhantomData<V>,
|
||||
}
|
||||
|
||||
impl<E, V: View> ComponentAdapter<V, E> {
|
||||
pub fn new(e: E) -> Self {
|
||||
Self {
|
||||
component: Some(e),
|
||||
element: None,
|
||||
phantom: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View, C: Component<V> + 'static> Element<V> for ComponentAdapter<V, C> {
|
||||
type LayoutState = ();
|
||||
|
||||
type PaintState = ();
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
if self.element.is_none() {
|
||||
let element = self
|
||||
.component
|
||||
.take()
|
||||
.expect("Component can only be rendered once")
|
||||
.render(view, cx.view_context());
|
||||
self.element = Some(element);
|
||||
}
|
||||
let constraint = self.element.as_mut().unwrap().layout(constraint, view, cx);
|
||||
(constraint, ())
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
self.element
|
||||
.as_mut()
|
||||
.expect("Layout should always be called before paint")
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx)
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
&self,
|
||||
range_utf16: std::ops::Range<usize>,
|
||||
_: RectF,
|
||||
_: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
view: &V,
|
||||
cx: &ViewContext<V>,
|
||||
) -> Option<RectF> {
|
||||
self.element
|
||||
.as_ref()
|
||||
.and_then(|el| el.rect_for_text_range(range_utf16, view, cx))
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
_: RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
view: &V,
|
||||
cx: &ViewContext<V>,
|
||||
) -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"type": "ComponentAdapter",
|
||||
"child": self.element.as_ref().map(|el| el.debug(view, cx)),
|
||||
})
|
||||
}
|
||||
}
|
|
@ -37,6 +37,15 @@ pub struct ContainerStyle {
|
|||
pub cursor: Option<CursorStyle>,
|
||||
}
|
||||
|
||||
impl ContainerStyle {
|
||||
pub fn fill(color: Color) -> Self {
|
||||
Self {
|
||||
background_color: Some(color),
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Container<V> {
|
||||
child: AnyElement<V>,
|
||||
style: ContainerStyle,
|
||||
|
|
|
@ -11,12 +11,12 @@ use crate::{
|
|||
MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
|
||||
},
|
||||
AnyElement, Element, EventContext, LayoutContext, MouseRegion, MouseState, PaintContext,
|
||||
SceneBuilder, SizeConstraint, ViewContext,
|
||||
SceneBuilder, SizeConstraint, TypeTag, ViewContext,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::{marker::PhantomData, ops::Range};
|
||||
use std::ops::Range;
|
||||
|
||||
pub struct MouseEventHandler<Tag: 'static, V> {
|
||||
pub struct MouseEventHandler<V: 'static> {
|
||||
child: AnyElement<V>,
|
||||
region_id: usize,
|
||||
cursor_style: Option<CursorStyle>,
|
||||
|
@ -26,13 +26,13 @@ pub struct MouseEventHandler<Tag: 'static, V> {
|
|||
notify_on_click: bool,
|
||||
above: bool,
|
||||
padding: Padding,
|
||||
_tag: PhantomData<Tag>,
|
||||
tag: TypeTag,
|
||||
}
|
||||
|
||||
/// Element which provides a render_child callback with a MouseState and paints a mouse
|
||||
/// region under (or above) it for easy mouse event handling.
|
||||
impl<Tag, V: 'static> MouseEventHandler<Tag, V> {
|
||||
pub fn for_child(child: impl Element<V>, region_id: usize) -> Self {
|
||||
impl<V: 'static> MouseEventHandler<V> {
|
||||
pub fn for_child<Tag: 'static>(child: impl Element<V>, region_id: usize) -> Self {
|
||||
Self {
|
||||
child: child.into_any(),
|
||||
region_id,
|
||||
|
@ -43,16 +43,19 @@ impl<Tag, V: 'static> MouseEventHandler<Tag, V> {
|
|||
hoverable: false,
|
||||
above: false,
|
||||
padding: Default::default(),
|
||||
_tag: PhantomData,
|
||||
tag: TypeTag::new::<Tag>(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new<E, F>(region_id: usize, cx: &mut ViewContext<V>, render_child: F) -> Self
|
||||
pub fn new<Tag: 'static, E>(
|
||||
region_id: usize,
|
||||
cx: &mut ViewContext<V>,
|
||||
render_child: impl FnOnce(&mut MouseState, &mut ViewContext<V>) -> E,
|
||||
) -> Self
|
||||
where
|
||||
E: Element<V>,
|
||||
F: FnOnce(&mut MouseState, &mut ViewContext<V>) -> E,
|
||||
{
|
||||
let mut mouse_state = cx.mouse_state::<Tag>(region_id);
|
||||
let mut mouse_state = cx.mouse_state_dynamic(TypeTag::new::<Tag>(), region_id);
|
||||
let child = render_child(&mut mouse_state, cx).into_any();
|
||||
let notify_on_hover = mouse_state.accessed_hovered();
|
||||
let notify_on_click = mouse_state.accessed_clicked();
|
||||
|
@ -66,19 +69,46 @@ impl<Tag, V: 'static> MouseEventHandler<Tag, V> {
|
|||
hoverable: true,
|
||||
above: false,
|
||||
padding: Default::default(),
|
||||
_tag: PhantomData,
|
||||
tag: TypeTag::new::<Tag>(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_dynamic(
|
||||
tag: TypeTag,
|
||||
region_id: usize,
|
||||
cx: &mut ViewContext<V>,
|
||||
render_child: impl FnOnce(&mut MouseState, &mut ViewContext<V>) -> AnyElement<V>,
|
||||
) -> Self {
|
||||
let mut mouse_state = cx.mouse_state_dynamic(tag, region_id);
|
||||
let child = render_child(&mut mouse_state, cx);
|
||||
let notify_on_hover = mouse_state.accessed_hovered();
|
||||
let notify_on_click = mouse_state.accessed_clicked();
|
||||
Self {
|
||||
child,
|
||||
region_id,
|
||||
cursor_style: None,
|
||||
handlers: Default::default(),
|
||||
notify_on_hover,
|
||||
notify_on_click,
|
||||
hoverable: true,
|
||||
above: false,
|
||||
padding: Default::default(),
|
||||
tag,
|
||||
}
|
||||
}
|
||||
|
||||
/// Modifies the MouseEventHandler to render the MouseRegion above the child element. Useful
|
||||
/// for drag and drop handling and similar events which should be captured before the child
|
||||
/// gets the opportunity
|
||||
pub fn above<D, F>(region_id: usize, cx: &mut ViewContext<V>, render_child: F) -> Self
|
||||
pub fn above<Tag: 'static, D>(
|
||||
region_id: usize,
|
||||
cx: &mut ViewContext<V>,
|
||||
render_child: impl FnOnce(&mut MouseState, &mut ViewContext<V>) -> D,
|
||||
) -> Self
|
||||
where
|
||||
D: Element<V>,
|
||||
F: FnOnce(&mut MouseState, &mut ViewContext<V>) -> D,
|
||||
{
|
||||
let mut handler = Self::new(region_id, cx, render_child);
|
||||
let mut handler = Self::new::<Tag, _>(region_id, cx, render_child);
|
||||
handler.above = true;
|
||||
handler
|
||||
}
|
||||
|
@ -223,7 +253,8 @@ impl<Tag, V: 'static> MouseEventHandler<Tag, V> {
|
|||
});
|
||||
}
|
||||
scene.push_mouse_region(
|
||||
MouseRegion::from_handlers::<Tag>(
|
||||
MouseRegion::from_handlers(
|
||||
self.tag,
|
||||
cx.view_id(),
|
||||
self.region_id,
|
||||
hit_bounds,
|
||||
|
@ -236,7 +267,7 @@ impl<Tag, V: 'static> MouseEventHandler<Tag, V> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<Tag, V: 'static> Element<V> for MouseEventHandler<Tag, V> {
|
||||
impl<V: 'static> Element<V> for MouseEventHandler<V> {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use collections::HashMap;
|
||||
use pathfinder_geometry::vector::{vec2f, Vector2F};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
geometry::rect::RectF,
|
||||
platform::{CursorStyle, MouseButton},
|
||||
scene::MouseDrag,
|
||||
AnyElement, Axis, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder,
|
||||
SizeConstraint, ViewContext,
|
||||
AnyElement, AppContext, Axis, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder,
|
||||
SizeConstraint, TypeTag, View, ViewContext,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -27,15 +27,6 @@ impl HandleSide {
|
|||
}
|
||||
}
|
||||
|
||||
/// 'before' is in reference to the standard english document ordering of left-to-right
|
||||
/// then top-to-bottom
|
||||
fn before_content(self) -> bool {
|
||||
match self {
|
||||
HandleSide::Left | HandleSide::Top => true,
|
||||
HandleSide::Right | HandleSide::Bottom => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn relevant_component(&self, vector: Vector2F) -> f32 {
|
||||
match self.axis() {
|
||||
Axis::Horizontal => vector.x(),
|
||||
|
@ -43,14 +34,6 @@ impl HandleSide {
|
|||
}
|
||||
}
|
||||
|
||||
fn compute_delta(&self, e: MouseDrag) -> f32 {
|
||||
if self.before_content() {
|
||||
self.relevant_component(e.prev_mouse_position) - self.relevant_component(e.position)
|
||||
} else {
|
||||
self.relevant_component(e.position) - self.relevant_component(e.prev_mouse_position)
|
||||
}
|
||||
}
|
||||
|
||||
fn of_rect(&self, bounds: RectF, handle_size: f32) -> RectF {
|
||||
match self {
|
||||
HandleSide::Top => RectF::new(bounds.origin(), vec2f(bounds.width(), handle_size)),
|
||||
|
@ -69,21 +52,29 @@ impl HandleSide {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct Resizable<V> {
|
||||
fn get_bounds(tag: TypeTag, cx: &AppContext) -> Option<&(RectF, RectF)>
|
||||
where
|
||||
{
|
||||
cx.optional_global::<ProviderMap>()
|
||||
.and_then(|map| map.0.get(&tag))
|
||||
}
|
||||
|
||||
pub struct Resizable<V: 'static> {
|
||||
child: AnyElement<V>,
|
||||
tag: TypeTag,
|
||||
handle_side: HandleSide,
|
||||
handle_size: f32,
|
||||
on_resize: Rc<RefCell<dyn FnMut(&mut V, f32, &mut ViewContext<V>)>>,
|
||||
on_resize: Rc<RefCell<dyn FnMut(&mut V, Option<f32>, &mut ViewContext<V>)>>,
|
||||
}
|
||||
|
||||
const DEFAULT_HANDLE_SIZE: f32 = 4.0;
|
||||
|
||||
impl<V: 'static> Resizable<V> {
|
||||
pub fn new(
|
||||
pub fn new<Tag: 'static>(
|
||||
child: AnyElement<V>,
|
||||
handle_side: HandleSide,
|
||||
size: f32,
|
||||
on_resize: impl 'static + FnMut(&mut V, f32, &mut ViewContext<V>),
|
||||
on_resize: impl 'static + FnMut(&mut V, Option<f32>, &mut ViewContext<V>),
|
||||
) -> Self {
|
||||
let child = match handle_side.axis() {
|
||||
Axis::Horizontal => child.constrained().with_max_width(size),
|
||||
|
@ -94,6 +85,7 @@ impl<V: 'static> Resizable<V> {
|
|||
Self {
|
||||
child,
|
||||
handle_side,
|
||||
tag: TypeTag::new::<Tag>(),
|
||||
handle_size: DEFAULT_HANDLE_SIZE,
|
||||
on_resize: Rc::new(RefCell::new(on_resize)),
|
||||
}
|
||||
|
@ -139,6 +131,14 @@ impl<V: 'static> Element<V> for Resizable<V> {
|
|||
handle_region,
|
||||
)
|
||||
.on_down(MouseButton::Left, |_, _: &mut V, _| {}) // This prevents the mouse down event from being propagated elsewhere
|
||||
.on_click(MouseButton::Left, {
|
||||
let on_resize = self.on_resize.clone();
|
||||
move |click, v, cx| {
|
||||
if click.click_count == 2 {
|
||||
on_resize.borrow_mut()(v, None, cx);
|
||||
}
|
||||
}
|
||||
})
|
||||
.on_drag(MouseButton::Left, {
|
||||
let bounds = bounds.clone();
|
||||
let side = self.handle_side;
|
||||
|
@ -146,16 +146,30 @@ impl<V: 'static> Element<V> for Resizable<V> {
|
|||
let min_size = side.relevant_component(constraint.min);
|
||||
let max_size = side.relevant_component(constraint.max);
|
||||
let on_resize = self.on_resize.clone();
|
||||
let tag = self.tag;
|
||||
move |event, view: &mut V, cx| {
|
||||
if event.end {
|
||||
return;
|
||||
}
|
||||
let new_size = min_size
|
||||
.max(prev_size + side.compute_delta(event))
|
||||
.min(max_size)
|
||||
.round();
|
||||
|
||||
let Some((bounds, _)) = get_bounds(tag, cx) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let new_size_raw = match side {
|
||||
// Handle on top side of element => Element is on bottom
|
||||
HandleSide::Top => bounds.height() + bounds.origin_y() - event.position.y(),
|
||||
// Handle on right side of element => Element is on left
|
||||
HandleSide::Right => event.position.x() - bounds.lower_left().x(),
|
||||
// Handle on left side of element => Element is on the right
|
||||
HandleSide::Left => bounds.width() + bounds.origin_x() - event.position.x(),
|
||||
// Handle on bottom side of element => Element is on the top
|
||||
HandleSide::Bottom => event.position.y() - bounds.lower_left().y(),
|
||||
};
|
||||
|
||||
let new_size = min_size.max(new_size_raw).min(max_size).round();
|
||||
if new_size != prev_size {
|
||||
on_resize.borrow_mut()(view, new_size, cx);
|
||||
on_resize.borrow_mut()(view, Some(new_size), cx);
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
@ -201,3 +215,80 @@ impl<V: 'static> Element<V> for Resizable<V> {
|
|||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
struct ProviderMap(HashMap<TypeTag, (RectF, RectF)>);
|
||||
|
||||
pub struct BoundsProvider<V: 'static, P> {
|
||||
child: AnyElement<V>,
|
||||
phantom: std::marker::PhantomData<P>,
|
||||
}
|
||||
|
||||
impl<V: 'static, P: 'static> BoundsProvider<V, P> {
|
||||
pub fn new(child: AnyElement<V>) -> Self {
|
||||
Self {
|
||||
child,
|
||||
phantom: std::marker::PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View, P: 'static> Element<V> for BoundsProvider<V, P> {
|
||||
type LayoutState = ();
|
||||
|
||||
type PaintState = ();
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
constraint: crate::SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut crate::LayoutContext<V>,
|
||||
) -> (pathfinder_geometry::vector::Vector2F, Self::LayoutState) {
|
||||
(self.child.layout(constraint, view, cx), ())
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut crate::SceneBuilder,
|
||||
bounds: pathfinder_geometry::rect::RectF,
|
||||
visible_bounds: pathfinder_geometry::rect::RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut crate::PaintContext<V>,
|
||||
) -> Self::PaintState {
|
||||
cx.update_default_global::<ProviderMap, _, _>(|map, _| {
|
||||
map.0.insert(TypeTag::new::<P>(), (bounds, visible_bounds));
|
||||
});
|
||||
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx)
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
&self,
|
||||
range_utf16: std::ops::Range<usize>,
|
||||
_: pathfinder_geometry::rect::RectF,
|
||||
_: pathfinder_geometry::rect::RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
view: &V,
|
||||
cx: &crate::ViewContext<V>,
|
||||
) -> Option<pathfinder_geometry::rect::RectF> {
|
||||
self.child.rect_for_text_range(range_utf16, view, cx)
|
||||
}
|
||||
|
||||
fn debug(
|
||||
&self,
|
||||
_: pathfinder_geometry::rect::RectF,
|
||||
_: &Self::LayoutState,
|
||||
_: &Self::PaintState,
|
||||
view: &V,
|
||||
cx: &crate::ViewContext<V>,
|
||||
) -> serde_json::Value {
|
||||
serde_json::json!({
|
||||
"type": "Provider",
|
||||
"providing": format!("{:?}", TypeTag::new::<P>()),
|
||||
"child": self.child.debug(view, cx),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use crate::{
|
|||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::json,
|
||||
Action, Axis, ElementStateHandle, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
Task, ViewContext,
|
||||
Task, TypeTag, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
|
@ -61,11 +61,23 @@ impl<V: 'static> Tooltip<V> {
|
|||
child: AnyElement<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self {
|
||||
struct ElementState<Tag>(Tag);
|
||||
struct MouseEventHandlerState<Tag>(Tag);
|
||||
Self::new_dynamic(TypeTag::new::<Tag>(), id, text, action, style, child, cx)
|
||||
}
|
||||
|
||||
pub fn new_dynamic(
|
||||
mut tag: TypeTag,
|
||||
id: usize,
|
||||
text: impl Into<Cow<'static, str>>,
|
||||
action: Option<Box<dyn Action>>,
|
||||
style: TooltipStyle,
|
||||
child: AnyElement<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self {
|
||||
tag = tag.compose(TypeTag::new::<Self>());
|
||||
|
||||
let focused_view_id = cx.focused_view_id();
|
||||
|
||||
let state_handle = cx.default_element_state::<ElementState<Tag>, Rc<TooltipState>>(id);
|
||||
let state_handle = cx.default_element_state_dynamic::<Rc<TooltipState>>(tag, id);
|
||||
let state = state_handle.read(cx).clone();
|
||||
let text = text.into();
|
||||
|
||||
|
@ -95,7 +107,7 @@ impl<V: 'static> Tooltip<V> {
|
|||
} else {
|
||||
None
|
||||
};
|
||||
let child = MouseEventHandler::<MouseEventHandlerState<Tag>, _>::new(id, cx, |_, _| child)
|
||||
let child = MouseEventHandler::new_dynamic(tag, id, cx, |_, _| child)
|
||||
.on_hover(move |e, _, cx| {
|
||||
let position = e.position;
|
||||
if e.started {
|
||||
|
|
|
@ -74,6 +74,13 @@ pub struct TextStyle {
|
|||
}
|
||||
|
||||
impl TextStyle {
|
||||
pub fn for_color(color: Color) -> Self {
|
||||
Self {
|
||||
color,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refine(self, refinement: TextStyleRefinement) -> TextStyle {
|
||||
TextStyle {
|
||||
color: refinement.color.unwrap_or(self.color),
|
||||
|
|
|
@ -24,6 +24,7 @@ use crate::{
|
|||
use anyhow::{anyhow, bail, Result};
|
||||
use async_task::Runnable;
|
||||
pub use event::*;
|
||||
use pathfinder_geometry::vector::vec2f;
|
||||
use postage::oneshot;
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
|
@ -134,6 +135,7 @@ pub trait InputHandler {
|
|||
pub trait Screen: Debug {
|
||||
fn as_any(&self) -> &dyn Any;
|
||||
fn bounds(&self) -> RectF;
|
||||
fn content_bounds(&self) -> RectF;
|
||||
fn display_uuid(&self) -> Option<Uuid>;
|
||||
}
|
||||
|
||||
|
@ -180,6 +182,16 @@ pub struct WindowOptions<'a> {
|
|||
pub screen: Option<Rc<dyn Screen>>,
|
||||
}
|
||||
|
||||
impl<'a> WindowOptions<'a> {
|
||||
pub fn with_bounds(bounds: Vector2F) -> Self {
|
||||
Self {
|
||||
bounds: WindowBounds::Fixed(RectF::new(vec2f(0., 0.), bounds)),
|
||||
center: true,
|
||||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct TitlebarOptions<'a> {
|
||||
pub title: Option<&'a str>,
|
||||
|
|
|
@ -3,10 +3,7 @@ use cocoa::{
|
|||
foundation::{NSPoint, NSRect},
|
||||
};
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
use pathfinder_geometry::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
};
|
||||
use pathfinder_geometry::vector::{vec2f, Vector2F};
|
||||
|
||||
///! Macos screen have a y axis that goings up from the bottom of the screen and
|
||||
///! an origin at the bottom left of the main display.
|
||||
|
@ -15,6 +12,7 @@ pub trait Vector2FExt {
|
|||
/// Converts self to an NSPoint with y axis pointing up.
|
||||
fn to_screen_ns_point(&self, native_window: id, window_height: f64) -> NSPoint;
|
||||
}
|
||||
|
||||
impl Vector2FExt for Vector2F {
|
||||
fn to_screen_ns_point(&self, native_window: id, window_height: f64) -> NSPoint {
|
||||
unsafe {
|
||||
|
@ -25,16 +23,13 @@ impl Vector2FExt for Vector2F {
|
|||
}
|
||||
|
||||
pub trait NSRectExt {
|
||||
fn to_rectf(&self) -> RectF;
|
||||
fn size_vec(&self) -> Vector2F;
|
||||
fn intersects(&self, other: Self) -> bool;
|
||||
}
|
||||
|
||||
impl NSRectExt for NSRect {
|
||||
fn to_rectf(&self) -> RectF {
|
||||
RectF::new(
|
||||
vec2f(self.origin.x as f32, self.origin.y as f32),
|
||||
vec2f(self.size.width as f32, self.size.height as f32),
|
||||
)
|
||||
fn size_vec(&self) -> Vector2F {
|
||||
vec2f(self.size.width as f32, self.size.height as f32)
|
||||
}
|
||||
|
||||
fn intersects(&self, other: Self) -> bool {
|
||||
|
|
|
@ -1,21 +1,19 @@
|
|||
use std::{any::Any, ffi::c_void};
|
||||
|
||||
use super::ns_string;
|
||||
use crate::platform;
|
||||
use cocoa::{
|
||||
appkit::NSScreen,
|
||||
base::{id, nil},
|
||||
foundation::{NSArray, NSDictionary},
|
||||
foundation::{NSArray, NSDictionary, NSPoint, NSRect, NSSize},
|
||||
};
|
||||
use core_foundation::{
|
||||
number::{kCFNumberIntType, CFNumberGetValue, CFNumberRef},
|
||||
uuid::{CFUUIDGetUUIDBytes, CFUUIDRef},
|
||||
};
|
||||
use core_graphics::display::CGDirectDisplayID;
|
||||
use pathfinder_geometry::rect::RectF;
|
||||
use pathfinder_geometry::{rect::RectF, vector::vec2f};
|
||||
use std::{any::Any, ffi::c_void};
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::{geometry::NSRectExt, ns_string};
|
||||
|
||||
#[link(name = "ApplicationServices", kind = "framework")]
|
||||
extern "C" {
|
||||
pub fn CGDisplayCreateUUIDFromDisplayID(display: CGDirectDisplayID) -> CFUUIDRef;
|
||||
|
@ -27,29 +25,58 @@ pub struct Screen {
|
|||
}
|
||||
|
||||
impl Screen {
|
||||
/// Get the screen with the given UUID.
|
||||
pub fn find_by_id(uuid: Uuid) -> Option<Self> {
|
||||
Self::all().find(|screen| platform::Screen::display_uuid(screen) == Some(uuid))
|
||||
}
|
||||
|
||||
/// Get the primary screen - the one with the menu bar, and whose bottom left
|
||||
/// corner is at the origin of the AppKit coordinate system.
|
||||
fn primary() -> Self {
|
||||
Self::all().next().unwrap()
|
||||
}
|
||||
|
||||
pub fn all() -> impl Iterator<Item = Self> {
|
||||
unsafe {
|
||||
let native_screens = NSScreen::screens(nil);
|
||||
(0..NSArray::count(native_screens))
|
||||
.into_iter()
|
||||
.map(|ix| Screen {
|
||||
native_screen: native_screens.objectAtIndex(ix),
|
||||
})
|
||||
.find(|screen| platform::Screen::display_uuid(screen) == Some(uuid))
|
||||
(0..NSArray::count(native_screens)).map(move |ix| Screen {
|
||||
native_screen: native_screens.objectAtIndex(ix),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all() -> Vec<Self> {
|
||||
let mut screens = Vec::new();
|
||||
unsafe {
|
||||
let native_screens = NSScreen::screens(nil);
|
||||
for ix in 0..NSArray::count(native_screens) {
|
||||
screens.push(Screen {
|
||||
native_screen: native_screens.objectAtIndex(ix),
|
||||
});
|
||||
}
|
||||
}
|
||||
screens
|
||||
/// Convert the given rectangle in screen coordinates from GPUI's
|
||||
/// coordinate system to the AppKit coordinate system.
|
||||
///
|
||||
/// In GPUI's coordinates, the origin is at the top left of the primary screen, with
|
||||
/// the Y axis pointing downward. In the AppKit coordindate system, the origin is at the
|
||||
/// bottom left of the primary screen, with the Y axis pointing upward.
|
||||
pub(crate) fn screen_rect_to_native(rect: RectF) -> NSRect {
|
||||
let primary_screen_height = unsafe { Self::primary().native_screen.frame().size.height };
|
||||
NSRect::new(
|
||||
NSPoint::new(
|
||||
rect.origin_x() as f64,
|
||||
primary_screen_height - rect.origin_y() as f64 - rect.height() as f64,
|
||||
),
|
||||
NSSize::new(rect.width() as f64, rect.height() as f64),
|
||||
)
|
||||
}
|
||||
|
||||
/// Convert the given rectangle in screen coordinates from the AppKit
|
||||
/// coordinate system to GPUI's coordinate system.
|
||||
///
|
||||
/// In GPUI's coordinates, the origin is at the top left of the primary screen, with
|
||||
/// the Y axis pointing downward. In the AppKit coordindate system, the origin is at the
|
||||
/// bottom left of the primary screen, with the Y axis pointing upward.
|
||||
pub(crate) fn screen_rect_from_native(rect: NSRect) -> RectF {
|
||||
let primary_screen_height = unsafe { Self::primary().native_screen.frame().size.height };
|
||||
RectF::new(
|
||||
vec2f(
|
||||
rect.origin.x as f32,
|
||||
(primary_screen_height - rect.origin.y - rect.size.height) as f32,
|
||||
),
|
||||
vec2f(rect.size.width as f32, rect.size.height as f32),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -108,9 +135,10 @@ impl platform::Screen for Screen {
|
|||
}
|
||||
|
||||
fn bounds(&self) -> RectF {
|
||||
unsafe {
|
||||
let frame = self.native_screen.frame();
|
||||
frame.to_rectf()
|
||||
}
|
||||
unsafe { Self::screen_rect_from_native(self.native_screen.frame()) }
|
||||
}
|
||||
|
||||
fn content_bounds(&self) -> RectF {
|
||||
unsafe { Self::screen_rect_from_native(self.native_screen.visibleFrame()) }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -368,32 +368,20 @@ impl WindowState {
|
|||
return WindowBounds::Fullscreen;
|
||||
}
|
||||
|
||||
let window_frame = self.frame();
|
||||
let screen_frame = self.native_window.screen().visibleFrame().to_rectf();
|
||||
if window_frame.size() == screen_frame.size() {
|
||||
let frame = self.frame();
|
||||
let screen_size = self.native_window.screen().visibleFrame().size_vec();
|
||||
if frame.size() == screen_size {
|
||||
WindowBounds::Maximized
|
||||
} else {
|
||||
WindowBounds::Fixed(window_frame)
|
||||
WindowBounds::Fixed(frame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the window bounds in window coordinates
|
||||
fn frame(&self) -> RectF {
|
||||
unsafe {
|
||||
let screen_frame = self.native_window.screen().visibleFrame();
|
||||
let window_frame = NSWindow::frame(self.native_window);
|
||||
RectF::new(
|
||||
vec2f(
|
||||
window_frame.origin.x as f32,
|
||||
(screen_frame.size.height - window_frame.origin.y - window_frame.size.height)
|
||||
as f32,
|
||||
),
|
||||
vec2f(
|
||||
window_frame.size.width as f32,
|
||||
window_frame.size.height as f32,
|
||||
),
|
||||
)
|
||||
let frame = NSWindow::frame(self.native_window);
|
||||
Screen::screen_rect_from_native(frame)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -480,21 +468,12 @@ impl MacWindow {
|
|||
native_window.setFrame_display_(screen.visibleFrame(), YES);
|
||||
}
|
||||
WindowBounds::Fixed(rect) => {
|
||||
let screen_frame = screen.visibleFrame();
|
||||
let ns_rect = NSRect::new(
|
||||
NSPoint::new(
|
||||
rect.origin_x() as f64,
|
||||
screen_frame.size.height
|
||||
- rect.origin_y() as f64
|
||||
- rect.height() as f64,
|
||||
),
|
||||
NSSize::new(rect.width() as f64, rect.height() as f64),
|
||||
);
|
||||
|
||||
if ns_rect.intersects(screen_frame) {
|
||||
native_window.setFrame_display_(ns_rect, YES);
|
||||
let bounds = Screen::screen_rect_to_native(rect);
|
||||
let screen_bounds = screen.visibleFrame();
|
||||
if bounds.intersects(screen_bounds) {
|
||||
native_window.setFrame_display_(bounds, YES);
|
||||
} else {
|
||||
native_window.setFrame_display_(screen_frame, YES);
|
||||
native_window.setFrame_display_(screen_bounds, YES);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -250,6 +250,10 @@ impl super::Screen for Screen {
|
|||
RectF::new(Vector2F::zero(), Vector2F::new(1920., 1080.))
|
||||
}
|
||||
|
||||
fn content_bounds(&self) -> RectF {
|
||||
RectF::new(Vector2F::zero(), Vector2F::new(1920., 1080.))
|
||||
}
|
||||
|
||||
fn display_uuid(&self) -> Option<uuid::Uuid> {
|
||||
Some(uuid::Uuid::new_v4())
|
||||
}
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
use crate::{platform::MouseButton, window::WindowContext, EventContext, ViewContext};
|
||||
use crate::{platform::MouseButton, window::WindowContext, EventContext, TypeTag, ViewContext};
|
||||
use collections::HashMap;
|
||||
use pathfinder_geometry::rect::RectF;
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
fmt::Debug,
|
||||
mem::Discriminant,
|
||||
rc::Rc,
|
||||
};
|
||||
use std::{any::Any, fmt::Debug, mem::Discriminant, rc::Rc};
|
||||
|
||||
use super::{
|
||||
mouse_event::{
|
||||
|
@ -33,14 +28,27 @@ impl MouseRegion {
|
|||
/// should pass a different (consistent) region_id. If you have one big region that covers your
|
||||
/// whole component, just pass the view_id again.
|
||||
pub fn new<Tag: 'static>(view_id: usize, region_id: usize, bounds: RectF) -> Self {
|
||||
Self::from_handlers::<Tag>(view_id, region_id, bounds, Default::default())
|
||||
Self::from_handlers(
|
||||
TypeTag::new::<Tag>(),
|
||||
view_id,
|
||||
region_id,
|
||||
bounds,
|
||||
Default::default(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn handle_all<Tag: 'static>(view_id: usize, region_id: usize, bounds: RectF) -> Self {
|
||||
Self::from_handlers::<Tag>(view_id, region_id, bounds, HandlerSet::capture_all())
|
||||
Self::from_handlers(
|
||||
TypeTag::new::<Tag>(),
|
||||
view_id,
|
||||
region_id,
|
||||
bounds,
|
||||
HandlerSet::capture_all(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn from_handlers<Tag: 'static>(
|
||||
pub fn from_handlers(
|
||||
tag: TypeTag,
|
||||
view_id: usize,
|
||||
region_id: usize,
|
||||
bounds: RectF,
|
||||
|
@ -49,10 +57,8 @@ impl MouseRegion {
|
|||
Self {
|
||||
id: MouseRegionId {
|
||||
view_id,
|
||||
tag: TypeId::of::<Tag>(),
|
||||
tag,
|
||||
region_id,
|
||||
#[cfg(debug_assertions)]
|
||||
tag_type_name: std::any::type_name::<Tag>(),
|
||||
},
|
||||
bounds,
|
||||
handlers,
|
||||
|
@ -180,20 +186,16 @@ impl MouseRegion {
|
|||
#[derive(Copy, Clone, Eq, PartialEq, Hash, Debug, PartialOrd, Ord)]
|
||||
pub struct MouseRegionId {
|
||||
view_id: usize,
|
||||
tag: TypeId,
|
||||
tag: TypeTag,
|
||||
region_id: usize,
|
||||
#[cfg(debug_assertions)]
|
||||
tag_type_name: &'static str,
|
||||
}
|
||||
|
||||
impl MouseRegionId {
|
||||
pub(crate) fn new<Tag: 'static>(view_id: usize, region_id: usize) -> Self {
|
||||
pub(crate) fn new(tag: TypeTag, view_id: usize, region_id: usize) -> Self {
|
||||
MouseRegionId {
|
||||
view_id,
|
||||
region_id,
|
||||
tag: TypeId::of::<Tag>(),
|
||||
#[cfg(debug_assertions)]
|
||||
tag_type_name: std::any::type_name::<Tag>(),
|
||||
tag,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -203,7 +205,7 @@ impl MouseRegionId {
|
|||
|
||||
#[cfg(debug_assertions)]
|
||||
pub fn tag_type_name(&self) -> &'static str {
|
||||
self.tag_type_name
|
||||
self.tag.type_name()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -106,7 +106,7 @@ impl View for Select {
|
|||
Default::default()
|
||||
};
|
||||
let mut result = Flex::column().with_child(
|
||||
MouseEventHandler::<Header, _>::new(self.handle.id(), cx, |mouse_state, cx| {
|
||||
MouseEventHandler::new::<Header, _>(self.handle.id(), cx, |mouse_state, cx| {
|
||||
(self.render_item)(
|
||||
self.selected_item_ix,
|
||||
ItemType::Header,
|
||||
|
@ -130,7 +130,7 @@ impl View for Select {
|
|||
let selected_item_ix = this.selected_item_ix;
|
||||
range.end = range.end.min(this.item_count);
|
||||
items.extend(range.map(|ix| {
|
||||
MouseEventHandler::<Item, _>::new(ix, cx, |mouse_state, cx| {
|
||||
MouseEventHandler::new::<Item, _>(ix, cx, |mouse_state, cx| {
|
||||
(this.render_item)(
|
||||
ix,
|
||||
if ix == selected_item_ix {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue