Merge remote-tracking branch 'origin/main' into chat-again
This commit is contained in:
commit
fe6f0a253b
164 changed files with 7012 additions and 3586 deletions
|
@ -10,7 +10,7 @@ mod window_input_handler;
|
|||
use crate::{
|
||||
elements::{AnyElement, AnyRootElement, RootElement},
|
||||
executor::{self, Task},
|
||||
fonts::TextStyle,
|
||||
image_cache::ImageCache,
|
||||
json,
|
||||
keymap_matcher::{self, Binding, KeymapContext, KeymapMatcher, Keystroke, MatchResult},
|
||||
platform::{
|
||||
|
@ -28,6 +28,7 @@ use collections::{hash_map::Entry, BTreeMap, HashMap, HashSet, VecDeque};
|
|||
use derive_more::Deref;
|
||||
pub use menu::*;
|
||||
use parking_lot::Mutex;
|
||||
use pathfinder_geometry::rect::RectF;
|
||||
use platform::Event;
|
||||
use postage::oneshot;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
|
@ -51,8 +52,12 @@ use std::{
|
|||
};
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub use test_app_context::{ContextHandle, TestAppContext};
|
||||
use util::ResultExt;
|
||||
use util::{
|
||||
http::{self, HttpClient},
|
||||
ResultExt,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
pub use window::MeasureParams;
|
||||
use window_input_handler::WindowInputHandler;
|
||||
|
||||
pub trait Entity: 'static {
|
||||
|
@ -154,12 +159,14 @@ impl App {
|
|||
let platform = platform::current::platform();
|
||||
let foreground = Rc::new(executor::Foreground::platform(platform.dispatcher())?);
|
||||
let foreground_platform = platform::current::foreground_platform(foreground.clone());
|
||||
let http_client = http::client();
|
||||
let app = Self(Rc::new(RefCell::new(AppContext::new(
|
||||
foreground,
|
||||
Arc::new(executor::Background::new()),
|
||||
platform.clone(),
|
||||
foreground_platform.clone(),
|
||||
Arc::new(FontCache::new(platform.fonts())),
|
||||
http_client,
|
||||
Default::default(),
|
||||
asset_source,
|
||||
))));
|
||||
|
@ -456,6 +463,7 @@ pub struct AppContext {
|
|||
pub asset_cache: Arc<AssetCache>,
|
||||
font_system: Arc<dyn FontSystem>,
|
||||
pub font_cache: Arc<FontCache>,
|
||||
pub image_cache: Arc<ImageCache>,
|
||||
action_deserializers: HashMap<&'static str, (TypeId, DeserializeActionCallback)>,
|
||||
capture_actions: HashMap<TypeId, HashMap<TypeId, Vec<Box<ActionCallback>>>>,
|
||||
// Entity Types -> { Action Types -> Action Handlers }
|
||||
|
@ -499,6 +507,7 @@ impl AppContext {
|
|||
platform: Arc<dyn platform::Platform>,
|
||||
foreground_platform: Rc<dyn platform::ForegroundPlatform>,
|
||||
font_cache: Arc<FontCache>,
|
||||
http_client: Arc<dyn HttpClient>,
|
||||
ref_counts: RefCounts,
|
||||
asset_source: impl AssetSource,
|
||||
) -> Self {
|
||||
|
@ -517,6 +526,7 @@ impl AppContext {
|
|||
platform,
|
||||
foreground_platform,
|
||||
font_cache,
|
||||
image_cache: Arc::new(ImageCache::new(http_client)),
|
||||
asset_cache: Arc::new(AssetCache::new(asset_source)),
|
||||
action_deserializers: Default::default(),
|
||||
capture_actions: Default::default(),
|
||||
|
@ -1898,7 +1908,6 @@ impl AppContext {
|
|||
|
||||
fn handle_repaint_window_effect(&mut self, window: AnyWindowHandle) {
|
||||
self.update_window(window, |cx| {
|
||||
cx.layout(false).log_err();
|
||||
if let Some(scene) = cx.paint().log_err() {
|
||||
cx.window.platform_window.present_scene(scene);
|
||||
}
|
||||
|
@ -3345,10 +3354,6 @@ impl<'a, 'b, V: 'static> ViewContext<'a, 'b, V> {
|
|||
self.element_state::<Tag, T>(element_id, T::default())
|
||||
}
|
||||
|
||||
pub fn rem_pixels(&self) -> f32 {
|
||||
16.
|
||||
}
|
||||
|
||||
pub fn default_element_state_dynamic<T: 'static + Default>(
|
||||
&mut self,
|
||||
tag: TypeTag,
|
||||
|
@ -3356,6 +3361,59 @@ impl<'a, 'b, V: 'static> ViewContext<'a, 'b, V> {
|
|||
) -> ElementStateHandle<T> {
|
||||
self.element_state_dynamic::<T>(tag, element_id, T::default())
|
||||
}
|
||||
|
||||
/// Return keystrokes that would dispatch the given action on the given view.
|
||||
pub(crate) fn keystrokes_for_action(
|
||||
&mut self,
|
||||
view_id: usize,
|
||||
action: &dyn Action,
|
||||
) -> Option<SmallVec<[Keystroke; 2]>> {
|
||||
self.notify_if_view_ancestors_change(view_id);
|
||||
|
||||
let window = self.window_handle;
|
||||
let mut contexts = Vec::new();
|
||||
let mut handler_depth = None;
|
||||
for (i, view_id) in self.ancestors(view_id).enumerate() {
|
||||
if let Some(view_metadata) = self.views_metadata.get(&(window, view_id)) {
|
||||
if let Some(actions) = self.actions.get(&view_metadata.type_id) {
|
||||
if actions.contains_key(&action.id()) {
|
||||
handler_depth = Some(i);
|
||||
}
|
||||
}
|
||||
contexts.push(view_metadata.keymap_context.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if self.global_actions.contains_key(&action.id()) {
|
||||
handler_depth = Some(contexts.len())
|
||||
}
|
||||
|
||||
let handler_depth = handler_depth.unwrap_or(0);
|
||||
(0..=handler_depth).find_map(|depth| {
|
||||
let contexts = &contexts[depth..];
|
||||
self.keystroke_matcher
|
||||
.keystrokes_for_action(action, contexts)
|
||||
})
|
||||
}
|
||||
|
||||
fn notify_if_view_ancestors_change(&mut self, view_id: usize) {
|
||||
let self_view_id = self.view_id;
|
||||
self.window
|
||||
.views_to_notify_if_ancestors_change
|
||||
.entry(view_id)
|
||||
.or_default()
|
||||
.push(self_view_id);
|
||||
}
|
||||
|
||||
pub fn paint_layer<F, R>(&mut self, clip_bounds: Option<RectF>, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut Self) -> R,
|
||||
{
|
||||
self.scene().push_layer(clip_bounds);
|
||||
let result = f(self);
|
||||
self.scene().pop_layer();
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: View> ViewContext<'_, '_, V> {
|
||||
|
@ -3447,267 +3505,6 @@ impl<V> BorrowWindowContext for ViewContext<'_, '_, V> {
|
|||
}
|
||||
}
|
||||
|
||||
/// Methods shared by both LayoutContext and PaintContext
|
||||
///
|
||||
/// It's that PaintContext should be implemented in terms of layout context and
|
||||
/// deref to it, in which case we wouldn't need this.
|
||||
pub trait RenderContext<'a, 'b, V> {
|
||||
fn text_style(&self) -> TextStyle;
|
||||
fn push_text_style(&mut self, style: TextStyle);
|
||||
fn pop_text_style(&mut self);
|
||||
fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V>;
|
||||
}
|
||||
|
||||
pub struct LayoutContext<'a, 'b, 'c, V> {
|
||||
// Nathan: Making this is public while I work on playground.
|
||||
pub view_context: &'c mut ViewContext<'a, 'b, V>,
|
||||
new_parents: &'c mut HashMap<usize, usize>,
|
||||
views_to_notify_if_ancestors_change: &'c mut HashMap<usize, SmallVec<[usize; 2]>>,
|
||||
text_style_stack: Vec<TextStyle>,
|
||||
pub refreshing: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, V> LayoutContext<'a, 'b, 'c, V> {
|
||||
pub fn new(
|
||||
view_context: &'c mut ViewContext<'a, 'b, V>,
|
||||
new_parents: &'c mut HashMap<usize, usize>,
|
||||
views_to_notify_if_ancestors_change: &'c mut HashMap<usize, SmallVec<[usize; 2]>>,
|
||||
refreshing: bool,
|
||||
) -> Self {
|
||||
Self {
|
||||
view_context,
|
||||
new_parents,
|
||||
views_to_notify_if_ancestors_change,
|
||||
text_style_stack: Vec::new(),
|
||||
refreshing,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
|
||||
self.view_context
|
||||
}
|
||||
|
||||
/// Return keystrokes that would dispatch the given action on the given view.
|
||||
pub(crate) fn keystrokes_for_action(
|
||||
&mut self,
|
||||
view_id: usize,
|
||||
action: &dyn Action,
|
||||
) -> Option<SmallVec<[Keystroke; 2]>> {
|
||||
self.notify_if_view_ancestors_change(view_id);
|
||||
|
||||
let window = self.window_handle;
|
||||
let mut contexts = Vec::new();
|
||||
let mut handler_depth = None;
|
||||
for (i, view_id) in self.ancestors(view_id).enumerate() {
|
||||
if let Some(view_metadata) = self.views_metadata.get(&(window, view_id)) {
|
||||
if let Some(actions) = self.actions.get(&view_metadata.type_id) {
|
||||
if actions.contains_key(&action.id()) {
|
||||
handler_depth = Some(i);
|
||||
}
|
||||
}
|
||||
contexts.push(view_metadata.keymap_context.clone());
|
||||
}
|
||||
}
|
||||
|
||||
if self.global_actions.contains_key(&action.id()) {
|
||||
handler_depth = Some(contexts.len())
|
||||
}
|
||||
|
||||
let action_contexts = if let Some(depth) = handler_depth {
|
||||
&contexts[depth..]
|
||||
} else {
|
||||
&contexts
|
||||
};
|
||||
|
||||
self.keystroke_matcher
|
||||
.keystrokes_for_action(action, action_contexts)
|
||||
}
|
||||
|
||||
fn notify_if_view_ancestors_change(&mut self, view_id: usize) {
|
||||
let self_view_id = self.view_id;
|
||||
self.views_to_notify_if_ancestors_change
|
||||
.entry(view_id)
|
||||
.or_default()
|
||||
.push(self_view_id);
|
||||
}
|
||||
|
||||
pub fn with_text_style<F, T>(&mut self, style: TextStyle, f: F) -> T
|
||||
where
|
||||
F: FnOnce(&mut Self) -> T,
|
||||
{
|
||||
self.push_text_style(style);
|
||||
let result = f(self);
|
||||
self.pop_text_style();
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, V> RenderContext<'a, 'b, V> for LayoutContext<'a, 'b, 'c, V> {
|
||||
fn text_style(&self) -> TextStyle {
|
||||
self.text_style_stack
|
||||
.last()
|
||||
.cloned()
|
||||
.unwrap_or(TextStyle::default(&self.font_cache))
|
||||
}
|
||||
|
||||
fn push_text_style(&mut self, style: TextStyle) {
|
||||
self.text_style_stack.push(style);
|
||||
}
|
||||
|
||||
fn pop_text_style(&mut self) {
|
||||
self.text_style_stack.pop();
|
||||
}
|
||||
|
||||
fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
|
||||
&mut self.view_context
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, V> Deref for LayoutContext<'a, 'b, 'c, V> {
|
||||
type Target = ViewContext<'a, 'b, V>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.view_context
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> DerefMut for LayoutContext<'_, '_, '_, V> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.view_context
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> BorrowAppContext for LayoutContext<'_, '_, '_, V> {
|
||||
fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
|
||||
BorrowAppContext::read_with(&*self.view_context, f)
|
||||
}
|
||||
|
||||
fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
|
||||
BorrowAppContext::update(&mut *self.view_context, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> BorrowWindowContext for LayoutContext<'_, '_, '_, V> {
|
||||
type Result<T> = T;
|
||||
|
||||
fn read_window<T, F: FnOnce(&WindowContext) -> T>(&self, window: AnyWindowHandle, f: F) -> T {
|
||||
BorrowWindowContext::read_window(&*self.view_context, window, f)
|
||||
}
|
||||
|
||||
fn read_window_optional<T, F>(&self, window: AnyWindowHandle, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(&WindowContext) -> Option<T>,
|
||||
{
|
||||
BorrowWindowContext::read_window_optional(&*self.view_context, window, f)
|
||||
}
|
||||
|
||||
fn update_window<T, F: FnOnce(&mut WindowContext) -> T>(
|
||||
&mut self,
|
||||
window: AnyWindowHandle,
|
||||
f: F,
|
||||
) -> T {
|
||||
BorrowWindowContext::update_window(&mut *self.view_context, window, f)
|
||||
}
|
||||
|
||||
fn update_window_optional<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(&mut WindowContext) -> Option<T>,
|
||||
{
|
||||
BorrowWindowContext::update_window_optional(&mut *self.view_context, window, f)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct PaintContext<'a, 'b, 'c, V> {
|
||||
pub view_context: &'c mut ViewContext<'a, 'b, V>,
|
||||
text_style_stack: Vec<TextStyle>,
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, V> PaintContext<'a, 'b, 'c, V> {
|
||||
pub fn new(view_context: &'c mut ViewContext<'a, 'b, V>) -> Self {
|
||||
Self {
|
||||
view_context,
|
||||
text_style_stack: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, V> RenderContext<'a, 'b, V> for PaintContext<'a, 'b, 'c, V> {
|
||||
fn text_style(&self) -> TextStyle {
|
||||
self.text_style_stack
|
||||
.last()
|
||||
.cloned()
|
||||
.unwrap_or(TextStyle::default(&self.font_cache))
|
||||
}
|
||||
|
||||
fn push_text_style(&mut self, style: TextStyle) {
|
||||
self.text_style_stack.push(style);
|
||||
}
|
||||
|
||||
fn pop_text_style(&mut self) {
|
||||
self.text_style_stack.pop();
|
||||
}
|
||||
|
||||
fn as_view_context(&mut self) -> &mut ViewContext<'a, 'b, V> {
|
||||
&mut self.view_context
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'b, 'c, V> Deref for PaintContext<'a, 'b, 'c, V> {
|
||||
type Target = ViewContext<'a, 'b, V>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
&self.view_context
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> DerefMut for PaintContext<'_, '_, '_, V> {
|
||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||
&mut self.view_context
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> BorrowAppContext for PaintContext<'_, '_, '_, V> {
|
||||
fn read_with<T, F: FnOnce(&AppContext) -> T>(&self, f: F) -> T {
|
||||
BorrowAppContext::read_with(&*self.view_context, f)
|
||||
}
|
||||
|
||||
fn update<T, F: FnOnce(&mut AppContext) -> T>(&mut self, f: F) -> T {
|
||||
BorrowAppContext::update(&mut *self.view_context, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<V> BorrowWindowContext for PaintContext<'_, '_, '_, V> {
|
||||
type Result<T> = T;
|
||||
|
||||
fn read_window<T, F>(&self, window: AnyWindowHandle, f: F) -> Self::Result<T>
|
||||
where
|
||||
F: FnOnce(&WindowContext) -> T,
|
||||
{
|
||||
BorrowWindowContext::read_window(self.view_context, window, f)
|
||||
}
|
||||
|
||||
fn read_window_optional<T, F>(&self, window: AnyWindowHandle, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(&WindowContext) -> Option<T>,
|
||||
{
|
||||
BorrowWindowContext::read_window_optional(self.view_context, window, f)
|
||||
}
|
||||
|
||||
fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Self::Result<T>
|
||||
where
|
||||
F: FnOnce(&mut WindowContext) -> T,
|
||||
{
|
||||
BorrowWindowContext::update_window(self.view_context, window, f)
|
||||
}
|
||||
|
||||
fn update_window_optional<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Option<T>
|
||||
where
|
||||
F: FnOnce(&mut WindowContext) -> Option<T>,
|
||||
{
|
||||
BorrowWindowContext::update_window_optional(self.view_context, window, f)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct EventContext<'a, 'b, 'c, V> {
|
||||
view_context: &'c mut ViewContext<'a, 'b, V>,
|
||||
pub(crate) handled: bool,
|
||||
|
@ -6499,7 +6296,7 @@ mod tests {
|
|||
|
||||
#[crate::test(self)]
|
||||
fn test_keystrokes_for_action(cx: &mut TestAppContext) {
|
||||
actions!(test, [Action1, Action2, GlobalAction]);
|
||||
actions!(test, [Action1, Action2, Action3, GlobalAction]);
|
||||
|
||||
struct View1 {
|
||||
child: ViewHandle<View2>,
|
||||
|
@ -6542,12 +6339,14 @@ mod tests {
|
|||
|
||||
cx.update(|cx| {
|
||||
cx.add_action(|_: &mut View1, _: &Action1, _cx| {});
|
||||
cx.add_action(|_: &mut View1, _: &Action3, _cx| {});
|
||||
cx.add_action(|_: &mut View2, _: &Action2, _cx| {});
|
||||
cx.add_global_action(|_: &GlobalAction, _| {});
|
||||
cx.add_bindings(vec![
|
||||
Binding::new("a", Action1, Some("View1")),
|
||||
Binding::new("b", Action2, Some("View1 > View2")),
|
||||
Binding::new("c", GlobalAction, Some("View3")), // View 3 does not exist
|
||||
Binding::new("c", Action3, Some("View2")),
|
||||
Binding::new("d", GlobalAction, Some("View3")), // View 3 does not exist
|
||||
]);
|
||||
});
|
||||
|
||||
|
@ -6555,47 +6354,40 @@ mod tests {
|
|||
view_1.update(cx, |_, cx| {
|
||||
view_2.update(cx, |_, cx| {
|
||||
// Sanity check
|
||||
let mut new_parents = Default::default();
|
||||
let mut notify_views_if_parents_change = Default::default();
|
||||
let mut layout_cx = LayoutContext::new(
|
||||
cx,
|
||||
&mut new_parents,
|
||||
&mut notify_views_if_parents_change,
|
||||
false,
|
||||
);
|
||||
assert_eq!(
|
||||
layout_cx
|
||||
.keystrokes_for_action(view_1_id, &Action1)
|
||||
cx.keystrokes_for_action(view_1_id, &Action1)
|
||||
.unwrap()
|
||||
.as_slice(),
|
||||
&[Keystroke::parse("a").unwrap()]
|
||||
);
|
||||
assert_eq!(
|
||||
layout_cx
|
||||
.keystrokes_for_action(view_2.id(), &Action2)
|
||||
cx.keystrokes_for_action(view_2.id(), &Action2)
|
||||
.unwrap()
|
||||
.as_slice(),
|
||||
&[Keystroke::parse("b").unwrap()]
|
||||
);
|
||||
assert_eq!(cx.keystrokes_for_action(view_1.id(), &Action3), None);
|
||||
assert_eq!(
|
||||
cx.keystrokes_for_action(view_2.id(), &Action3)
|
||||
.unwrap()
|
||||
.as_slice(),
|
||||
&[Keystroke::parse("c").unwrap()]
|
||||
);
|
||||
|
||||
// The 'a' keystroke propagates up the view tree from view_2
|
||||
// to view_1. The action, Action1, is handled by view_1.
|
||||
assert_eq!(
|
||||
layout_cx
|
||||
.keystrokes_for_action(view_2.id(), &Action1)
|
||||
cx.keystrokes_for_action(view_2.id(), &Action1)
|
||||
.unwrap()
|
||||
.as_slice(),
|
||||
&[Keystroke::parse("a").unwrap()]
|
||||
);
|
||||
|
||||
// Actions that are handled below the current view don't have bindings
|
||||
assert_eq!(layout_cx.keystrokes_for_action(view_1_id, &Action2), None);
|
||||
assert_eq!(cx.keystrokes_for_action(view_1_id, &Action2), None);
|
||||
|
||||
// Actions that are handled in other branches of the tree should not have a binding
|
||||
assert_eq!(
|
||||
layout_cx.keystrokes_for_action(view_2.id(), &GlobalAction),
|
||||
None
|
||||
);
|
||||
assert_eq!(cx.keystrokes_for_action(view_2.id(), &GlobalAction), None);
|
||||
});
|
||||
});
|
||||
|
||||
|
@ -6604,7 +6396,8 @@ mod tests {
|
|||
&available_actions(window.into(), view_1.id(), cx),
|
||||
&[
|
||||
("test::Action1", vec![Keystroke::parse("a").unwrap()]),
|
||||
("test::GlobalAction", vec![])
|
||||
("test::Action3", vec![]),
|
||||
("test::GlobalAction", vec![]),
|
||||
],
|
||||
);
|
||||
|
||||
|
@ -6614,6 +6407,7 @@ mod tests {
|
|||
&[
|
||||
("test::Action1", vec![Keystroke::parse("a").unwrap()]),
|
||||
("test::Action2", vec![Keystroke::parse("b").unwrap()]),
|
||||
("test::Action3", vec![Keystroke::parse("c").unwrap()]),
|
||||
("test::GlobalAction", vec![]),
|
||||
],
|
||||
);
|
||||
|
|
|
@ -57,6 +57,7 @@ impl TestAppContext {
|
|||
platform,
|
||||
foreground_platform.clone(),
|
||||
font_cache,
|
||||
util::http::FakeHttpClient::with_404_response(),
|
||||
RefCounts::new(leak_detector),
|
||||
(),
|
||||
);
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{
|
||||
elements::AnyRootElement,
|
||||
fonts::{TextStyle, TextStyleRefinement},
|
||||
geometry::{rect::RectF, Size},
|
||||
json::ToJson,
|
||||
keymap_matcher::{Binding, KeymapContext, Keystroke, MatchResult},
|
||||
|
@ -15,9 +16,8 @@ use crate::{
|
|||
text_layout::TextLayoutCache,
|
||||
util::post_inc,
|
||||
Action, AnyView, AnyViewHandle, AnyWindowHandle, AppContext, BorrowAppContext,
|
||||
BorrowWindowContext, Effect, Element, Entity, Handle, LayoutContext, MouseRegion,
|
||||
MouseRegionId, PaintContext, SceneBuilder, Subscription, View, ViewContext, ViewHandle,
|
||||
WindowInvalidation,
|
||||
BorrowWindowContext, Effect, Element, Entity, Handle, MouseRegion, MouseRegionId, SceneBuilder,
|
||||
Subscription, View, ViewContext, ViewHandle, WindowInvalidation,
|
||||
};
|
||||
use anyhow::{anyhow, bail, Result};
|
||||
use collections::{HashMap, HashSet};
|
||||
|
@ -30,7 +30,7 @@ use sqlez::{
|
|||
statement::Statement,
|
||||
};
|
||||
use std::{
|
||||
any::TypeId,
|
||||
any::{type_name, Any, TypeId},
|
||||
mem,
|
||||
ops::{Deref, DerefMut, Range, Sub},
|
||||
};
|
||||
|
@ -50,20 +50,28 @@ pub struct Window {
|
|||
pub(crate) parents: HashMap<usize, usize>,
|
||||
pub(crate) is_active: bool,
|
||||
pub(crate) is_fullscreen: bool,
|
||||
inspector_enabled: bool,
|
||||
pub(crate) invalidation: Option<WindowInvalidation>,
|
||||
pub(crate) platform_window: Box<dyn platform::Window>,
|
||||
pub(crate) rendered_views: HashMap<usize, Box<dyn AnyRootElement>>,
|
||||
scene: SceneBuilder,
|
||||
pub(crate) text_style_stack: Vec<TextStyle>,
|
||||
pub(crate) theme_stack: Vec<Box<dyn Any>>,
|
||||
pub(crate) new_parents: HashMap<usize, usize>,
|
||||
pub(crate) views_to_notify_if_ancestors_change: HashMap<usize, SmallVec<[usize; 2]>>,
|
||||
titlebar_height: f32,
|
||||
appearance: Appearance,
|
||||
cursor_regions: Vec<CursorRegion>,
|
||||
mouse_regions: Vec<(MouseRegion, usize)>,
|
||||
event_handlers: Vec<EventHandler>,
|
||||
last_mouse_moved_event: Option<Event>,
|
||||
last_mouse_position: Vector2F,
|
||||
pressed_buttons: HashSet<MouseButton>,
|
||||
pub(crate) hovered_region_ids: Vec<MouseRegionId>,
|
||||
pub(crate) clicked_region_ids: Vec<MouseRegionId>,
|
||||
pub(crate) clicked_region: Option<(MouseRegionId, MouseButton)>,
|
||||
mouse_position: Vector2F,
|
||||
text_layout_cache: TextLayoutCache,
|
||||
refreshing: bool,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
|
@ -87,19 +95,27 @@ impl Window {
|
|||
is_active: false,
|
||||
invalidation: None,
|
||||
is_fullscreen: false,
|
||||
inspector_enabled: false,
|
||||
platform_window,
|
||||
rendered_views: Default::default(),
|
||||
scene: SceneBuilder::new(),
|
||||
text_style_stack: Vec::new(),
|
||||
theme_stack: Vec::new(),
|
||||
new_parents: HashMap::default(),
|
||||
views_to_notify_if_ancestors_change: HashMap::default(),
|
||||
cursor_regions: Default::default(),
|
||||
mouse_regions: Default::default(),
|
||||
event_handlers: Default::default(),
|
||||
text_layout_cache: TextLayoutCache::new(cx.font_system.clone()),
|
||||
last_mouse_moved_event: None,
|
||||
last_mouse_position: Vector2F::zero(),
|
||||
pressed_buttons: Default::default(),
|
||||
hovered_region_ids: Default::default(),
|
||||
clicked_region_ids: Default::default(),
|
||||
clicked_region: None,
|
||||
mouse_position: vec2f(0., 0.),
|
||||
titlebar_height,
|
||||
appearance,
|
||||
refreshing: false,
|
||||
};
|
||||
|
||||
let mut window_context = WindowContext::mutable(cx, &mut window, handle);
|
||||
|
@ -226,6 +242,26 @@ impl<'a> WindowContext<'a> {
|
|||
.push_back(Effect::RepaintWindow { window });
|
||||
}
|
||||
|
||||
pub fn scene(&mut self) -> &mut SceneBuilder {
|
||||
&mut self.window.scene
|
||||
}
|
||||
|
||||
pub fn enable_inspector(&mut self) {
|
||||
self.window.inspector_enabled = true;
|
||||
}
|
||||
|
||||
pub fn is_inspector_enabled(&self) -> bool {
|
||||
self.window.inspector_enabled
|
||||
}
|
||||
|
||||
pub fn is_mouse_down(&self, button: MouseButton) -> bool {
|
||||
self.window.pressed_buttons.contains(&button)
|
||||
}
|
||||
|
||||
pub fn rem_size(&self) -> f32 {
|
||||
16.
|
||||
}
|
||||
|
||||
pub fn layout_engine(&mut self) -> Option<&mut LayoutEngine> {
|
||||
self.window.layout_engines.last_mut()
|
||||
}
|
||||
|
@ -259,7 +295,11 @@ impl<'a> WindowContext<'a> {
|
|||
}
|
||||
|
||||
pub fn mouse_position(&self) -> Vector2F {
|
||||
self.window.mouse_position
|
||||
self.window.platform_window.mouse_position()
|
||||
}
|
||||
|
||||
pub fn refreshing(&self) -> bool {
|
||||
self.window.refreshing
|
||||
}
|
||||
|
||||
pub fn text_layout_cache(&self) -> &TextLayoutCache {
|
||||
|
@ -507,7 +547,9 @@ impl<'a> WindowContext<'a> {
|
|||
}
|
||||
|
||||
pub(crate) fn dispatch_event(&mut self, event: Event, event_reused: bool) -> bool {
|
||||
self.dispatch_to_new_event_handlers(&event);
|
||||
if !event_reused {
|
||||
self.dispatch_event_2(&event);
|
||||
}
|
||||
|
||||
let mut mouse_events = SmallVec::<[_; 2]>::new();
|
||||
let mut notified_views: HashSet<usize> = Default::default();
|
||||
|
@ -576,7 +618,7 @@ impl<'a> WindowContext<'a> {
|
|||
// Synthesize one last drag event to end the drag
|
||||
mouse_events.push(MouseEvent::Drag(MouseDrag {
|
||||
region: Default::default(),
|
||||
prev_mouse_position: self.window.mouse_position,
|
||||
prev_mouse_position: self.window.last_mouse_position,
|
||||
platform_event: MouseMovedEvent {
|
||||
position: e.position,
|
||||
pressed_button: Some(e.button),
|
||||
|
@ -630,14 +672,14 @@ impl<'a> WindowContext<'a> {
|
|||
if pressed_button.is_some() {
|
||||
mouse_events.push(MouseEvent::Drag(MouseDrag {
|
||||
region: Default::default(),
|
||||
prev_mouse_position: self.window.mouse_position,
|
||||
prev_mouse_position: self.window.last_mouse_position,
|
||||
platform_event: e.clone(),
|
||||
end: false,
|
||||
}));
|
||||
} else if let Some((_, clicked_button)) = self.window.clicked_region {
|
||||
mouse_events.push(MouseEvent::Drag(MouseDrag {
|
||||
region: Default::default(),
|
||||
prev_mouse_position: self.window.mouse_position,
|
||||
prev_mouse_position: self.window.last_mouse_position,
|
||||
platform_event: e.clone(),
|
||||
end: true,
|
||||
}));
|
||||
|
@ -697,7 +739,7 @@ impl<'a> WindowContext<'a> {
|
|||
}
|
||||
|
||||
if let Some(position) = event.position() {
|
||||
self.window.mouse_position = position;
|
||||
self.window.last_mouse_position = position;
|
||||
}
|
||||
|
||||
// 2. Dispatch mouse events on regions
|
||||
|
@ -711,7 +753,7 @@ impl<'a> WindowContext<'a> {
|
|||
match &mouse_event {
|
||||
MouseEvent::Hover(_) => {
|
||||
let mut highest_z_index = None;
|
||||
let mouse_position = self.window.mouse_position.clone();
|
||||
let mouse_position = self.mouse_position();
|
||||
let window = &mut *self.window;
|
||||
let prev_hovered_regions = mem::take(&mut window.hovered_region_ids);
|
||||
for (region, z_index) in window.mouse_regions.iter().rev() {
|
||||
|
@ -756,7 +798,7 @@ impl<'a> WindowContext<'a> {
|
|||
|
||||
MouseEvent::Down(_) | MouseEvent::Up(_) => {
|
||||
for (region, _) in self.window.mouse_regions.iter().rev() {
|
||||
if region.bounds.contains_point(self.window.mouse_position) {
|
||||
if region.bounds.contains_point(self.mouse_position()) {
|
||||
valid_regions.push(region.clone());
|
||||
if region.notify_on_click {
|
||||
notified_views.insert(region.id().view_id());
|
||||
|
@ -783,10 +825,7 @@ impl<'a> WindowContext<'a> {
|
|||
// Find regions which still overlap with the mouse since the last MouseDown happened
|
||||
for (mouse_region, _) in self.window.mouse_regions.iter().rev() {
|
||||
if clicked_region_ids.contains(&mouse_region.id()) {
|
||||
if mouse_region
|
||||
.bounds
|
||||
.contains_point(self.window.mouse_position)
|
||||
{
|
||||
if mouse_region.bounds.contains_point(self.mouse_position()) {
|
||||
valid_regions.push(mouse_region.clone());
|
||||
} else {
|
||||
// Let the view know that it hasn't been clicked anymore
|
||||
|
@ -813,10 +852,7 @@ impl<'a> WindowContext<'a> {
|
|||
| MouseEvent::ClickOut(_) => {
|
||||
for (mouse_region, _) in self.window.mouse_regions.iter().rev() {
|
||||
// NOT contains
|
||||
if !mouse_region
|
||||
.bounds
|
||||
.contains_point(self.window.mouse_position)
|
||||
{
|
||||
if !mouse_region.bounds.contains_point(self.mouse_position()) {
|
||||
valid_regions.push(mouse_region.clone());
|
||||
}
|
||||
}
|
||||
|
@ -825,10 +861,7 @@ impl<'a> WindowContext<'a> {
|
|||
_ => {
|
||||
for (mouse_region, _) in self.window.mouse_regions.iter().rev() {
|
||||
// Contains
|
||||
if mouse_region
|
||||
.bounds
|
||||
.contains_point(self.window.mouse_position)
|
||||
{
|
||||
if mouse_region.bounds.contains_point(self.mouse_position()) {
|
||||
valid_regions.push(mouse_region.clone());
|
||||
}
|
||||
}
|
||||
|
@ -892,12 +925,24 @@ impl<'a> WindowContext<'a> {
|
|||
any_event_handled
|
||||
}
|
||||
|
||||
fn dispatch_to_new_event_handlers(&mut self, event: &Event) {
|
||||
fn dispatch_event_2(&mut self, event: &Event) {
|
||||
match event {
|
||||
Event::MouseDown(event) => {
|
||||
self.window.pressed_buttons.insert(event.button);
|
||||
}
|
||||
Event::MouseUp(event) => {
|
||||
self.window.pressed_buttons.remove(&event.button);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
if let Some(mouse_event) = event.mouse_event() {
|
||||
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() {
|
||||
(event_handler.handler)(mouse_event, self);
|
||||
if !(event_handler.handler)(mouse_event, self) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
self.window.event_handlers = event_handlers;
|
||||
|
@ -1000,21 +1045,17 @@ impl<'a> WindowContext<'a> {
|
|||
|
||||
let mut rendered_root = self.window.rendered_views.remove(&root_view_id).unwrap();
|
||||
|
||||
let mut new_parents = HashMap::default();
|
||||
let mut views_to_notify_if_ancestors_change = HashMap::default();
|
||||
rendered_root.layout(
|
||||
SizeConstraint::new(window_size, window_size),
|
||||
&mut new_parents,
|
||||
&mut views_to_notify_if_ancestors_change,
|
||||
refreshing,
|
||||
self,
|
||||
)?;
|
||||
self.window.refreshing = refreshing;
|
||||
rendered_root.layout(SizeConstraint::strict(window_size), self)?;
|
||||
self.window.refreshing = false;
|
||||
|
||||
let views_to_notify_if_ancestors_change =
|
||||
mem::take(&mut self.window.views_to_notify_if_ancestors_change);
|
||||
for (view_id, view_ids_to_notify) in views_to_notify_if_ancestors_change {
|
||||
let mut current_view_id = view_id;
|
||||
loop {
|
||||
let old_parent_id = self.window.parents.get(¤t_view_id);
|
||||
let new_parent_id = new_parents.get(¤t_view_id);
|
||||
let new_parent_id = self.window.new_parents.get(¤t_view_id);
|
||||
if old_parent_id.is_none() && new_parent_id.is_none() {
|
||||
break;
|
||||
} else if old_parent_id == new_parent_id {
|
||||
|
@ -1029,6 +1070,7 @@ impl<'a> WindowContext<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
let new_parents = mem::take(&mut self.window.new_parents);
|
||||
let old_parents = mem::replace(&mut self.window.parents, new_parents);
|
||||
self.window
|
||||
.rendered_views
|
||||
|
@ -1043,9 +1085,7 @@ impl<'a> WindowContext<'a> {
|
|||
let root_view_id = self.window.root_view().id();
|
||||
let mut rendered_root = self.window.rendered_views.remove(&root_view_id).unwrap();
|
||||
|
||||
let mut scene_builder = SceneBuilder::new(scale_factor);
|
||||
rendered_root.paint(
|
||||
&mut scene_builder,
|
||||
Vector2F::zero(),
|
||||
RectF::from_points(Vector2F::zero(), window_size),
|
||||
self,
|
||||
|
@ -1055,7 +1095,7 @@ impl<'a> WindowContext<'a> {
|
|||
.insert(root_view_id, rendered_root);
|
||||
|
||||
self.window.text_layout_cache.finish_frame();
|
||||
let mut scene = scene_builder.build();
|
||||
let mut scene = self.window.scene.build(scale_factor);
|
||||
self.window.cursor_regions = scene.cursor_regions();
|
||||
self.window.mouse_regions = scene.mouse_regions();
|
||||
self.window.event_handlers = scene.take_event_handlers();
|
||||
|
@ -1110,7 +1150,7 @@ impl<'a> WindowContext<'a> {
|
|||
self.window.is_fullscreen
|
||||
}
|
||||
|
||||
pub(crate) fn dispatch_action(&mut self, view_id: Option<usize>, action: &dyn Action) -> bool {
|
||||
pub fn dispatch_action(&mut self, view_id: Option<usize>, action: &dyn Action) -> bool {
|
||||
if let Some(view_id) = view_id {
|
||||
self.halt_action_dispatch = false;
|
||||
self.visit_dispatch_path(view_id, |view_id, capture_phase, cx| {
|
||||
|
@ -1203,6 +1243,10 @@ impl<'a> WindowContext<'a> {
|
|||
self.window.platform_window.bounds()
|
||||
}
|
||||
|
||||
pub fn titlebar_height(&self) -> f32 {
|
||||
self.window.titlebar_height
|
||||
}
|
||||
|
||||
pub fn window_appearance(&self) -> Appearance {
|
||||
self.window.appearance
|
||||
}
|
||||
|
@ -1274,6 +1318,43 @@ impl<'a> WindowContext<'a> {
|
|||
};
|
||||
handle
|
||||
}
|
||||
|
||||
pub fn text_style(&self) -> TextStyle {
|
||||
self.window
|
||||
.text_style_stack
|
||||
.last()
|
||||
.cloned()
|
||||
.unwrap_or(TextStyle::default(&self.font_cache))
|
||||
}
|
||||
|
||||
pub fn push_text_style(&mut self, refinement: &TextStyleRefinement) -> Result<()> {
|
||||
let mut style = self.text_style();
|
||||
style.refine(refinement, self.font_cache())?;
|
||||
self.window.text_style_stack.push(style);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn pop_text_style(&mut self) {
|
||||
self.window.text_style_stack.pop();
|
||||
}
|
||||
|
||||
pub fn theme<T: 'static>(&self) -> &T {
|
||||
self.window
|
||||
.theme_stack
|
||||
.iter()
|
||||
.rev()
|
||||
.find_map(|theme| theme.downcast_ref())
|
||||
.ok_or_else(|| anyhow!("no theme provided of type {}", type_name::<T>()))
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
pub fn push_theme<T: 'static>(&mut self, theme: T) {
|
||||
self.window.theme_stack.push(Box::new(theme));
|
||||
}
|
||||
|
||||
pub fn pop_theme(&mut self) {
|
||||
self.window.theme_stack.pop();
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
|
@ -1289,9 +1370,12 @@ impl LayoutEngine {
|
|||
where
|
||||
C: IntoIterator<Item = LayoutId>,
|
||||
{
|
||||
Ok(self
|
||||
.0
|
||||
.new_with_children(style, &children.into_iter().collect::<Vec<_>>())?)
|
||||
let children = children.into_iter().collect::<Vec<_>>();
|
||||
if children.is_empty() {
|
||||
Ok(self.0.new_leaf(style)?)
|
||||
} else {
|
||||
Ok(self.0.new_with_children(style, &children)?)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_measured_node<F>(&mut self, style: LayoutStyle, measure: F) -> Result<LayoutId>
|
||||
|
@ -1314,8 +1398,8 @@ impl LayoutEngine {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn computed_layout(&mut self, node: LayoutId) -> Result<EngineLayout> {
|
||||
Ok(self.0.layout(node)?.into())
|
||||
pub fn computed_layout(&mut self, node: LayoutId) -> Result<Layout> {
|
||||
Ok(Layout::from(self.0.layout(node)?))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1339,7 +1423,7 @@ where
|
|||
}
|
||||
|
||||
#[derive(Debug, Clone, Default)]
|
||||
pub struct EngineLayout {
|
||||
pub struct Layout {
|
||||
pub bounds: RectF,
|
||||
pub order: u32,
|
||||
}
|
||||
|
@ -1349,7 +1433,7 @@ pub struct MeasureParams {
|
|||
pub available_space: Size<AvailableSpace>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum AvailableSpace {
|
||||
/// The amount of space available is the specified number of pixels
|
||||
Pixels(f32),
|
||||
|
@ -1375,7 +1459,7 @@ impl From<taffy::prelude::AvailableSpace> for AvailableSpace {
|
|||
}
|
||||
}
|
||||
|
||||
impl From<&taffy::tree::Layout> for EngineLayout {
|
||||
impl From<&taffy::tree::Layout> for Layout {
|
||||
fn from(value: &taffy::tree::Layout) -> Self {
|
||||
Self {
|
||||
bounds: RectF::new(
|
||||
|
@ -1592,18 +1676,13 @@ impl<V: 'static> Element<V> for ChildView {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
_: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
if let Some(mut rendered_view) = cx.window.rendered_views.remove(&self.view_id) {
|
||||
cx.new_parents.insert(self.view_id, cx.view_id());
|
||||
let parent_id = cx.view_id();
|
||||
cx.window.new_parents.insert(self.view_id, parent_id);
|
||||
let size = rendered_view
|
||||
.layout(
|
||||
constraint,
|
||||
cx.new_parents,
|
||||
cx.views_to_notify_if_ancestors_change,
|
||||
cx.refreshing,
|
||||
cx.view_context,
|
||||
)
|
||||
.layout(constraint, cx)
|
||||
.log_err()
|
||||
.unwrap_or(Vector2F::zero());
|
||||
cx.window.rendered_views.insert(self.view_id, rendered_view);
|
||||
|
@ -1620,16 +1699,15 @@ impl<V: 'static> Element<V> for ChildView {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
if let Some(mut rendered_view) = cx.window.rendered_views.remove(&self.view_id) {
|
||||
rendered_view
|
||||
.paint(scene, bounds.origin(), visible_bounds, cx)
|
||||
.paint(bounds.origin(), visible_bounds, cx)
|
||||
.log_err();
|
||||
cx.window.rendered_views.insert(self.view_id, rendered_view);
|
||||
} else {
|
||||
|
|
|
@ -34,14 +34,12 @@ use crate::{
|
|||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json, Action, Entity, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, TypeTag, View,
|
||||
ViewContext, WeakViewHandle, WindowContext,
|
||||
json, Action, Entity, SizeConstraint, TypeTag, View, ViewContext, WeakViewHandle,
|
||||
WindowContext,
|
||||
};
|
||||
use anyhow::{anyhow, Result};
|
||||
use collections::HashMap;
|
||||
use core::panic;
|
||||
use json::ToJson;
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
any::{type_name, Any},
|
||||
borrow::Cow,
|
||||
|
@ -61,17 +59,16 @@ pub trait Element<V: 'static>: 'static {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState);
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
layout: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState;
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
@ -262,16 +259,15 @@ trait AnyElementState<V> {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Vector2F;
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
origin: Vector2F,
|
||||
visible_bounds: RectF,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
);
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
@ -314,7 +310,7 @@ impl<V, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Vector2F {
|
||||
let result;
|
||||
*self = match mem::take(self) {
|
||||
|
@ -348,11 +344,10 @@ impl<V, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
origin: Vector2F,
|
||||
visible_bounds: RectF,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
*self = match mem::take(self) {
|
||||
ElementState::PostLayout {
|
||||
|
@ -362,14 +357,7 @@ impl<V, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
|
|||
mut layout,
|
||||
} => {
|
||||
let bounds = RectF::new(origin, size);
|
||||
let paint = element.paint(
|
||||
scene,
|
||||
bounds,
|
||||
visible_bounds,
|
||||
&mut layout,
|
||||
view,
|
||||
&mut PaintContext::new(cx),
|
||||
);
|
||||
let paint = element.paint(bounds, visible_bounds, &mut layout, view, cx);
|
||||
ElementState::PostPaint {
|
||||
element,
|
||||
constraint,
|
||||
|
@ -387,14 +375,7 @@ impl<V, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
|
|||
..
|
||||
} => {
|
||||
let bounds = RectF::new(origin, bounds.size());
|
||||
let paint = element.paint(
|
||||
scene,
|
||||
bounds,
|
||||
visible_bounds,
|
||||
&mut layout,
|
||||
view,
|
||||
&mut PaintContext::new(cx),
|
||||
);
|
||||
let paint = element.paint(bounds, visible_bounds, &mut layout, view, cx);
|
||||
ElementState::PostPaint {
|
||||
element,
|
||||
constraint,
|
||||
|
@ -517,20 +498,19 @@ impl<V> AnyElement<V> {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Vector2F {
|
||||
self.state.layout(constraint, view, cx)
|
||||
}
|
||||
|
||||
pub fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
origin: Vector2F,
|
||||
visible_bounds: RectF,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
self.state.paint(scene, origin, visible_bounds, view, cx);
|
||||
self.state.paint(origin, visible_bounds, view, cx);
|
||||
}
|
||||
|
||||
pub fn rect_for_text_range(
|
||||
|
@ -578,7 +558,7 @@ impl<V: 'static> Element<V> for AnyElement<V> {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let size = self.layout(constraint, view, cx);
|
||||
(size, ())
|
||||
|
@ -586,14 +566,13 @@ impl<V: 'static> Element<V> for AnyElement<V> {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
self.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
self.paint(bounds.origin(), visible_bounds, view, cx);
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
@ -646,17 +625,9 @@ impl<V> RootElement<V> {
|
|||
}
|
||||
|
||||
pub trait AnyRootElement {
|
||||
fn layout(
|
||||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
new_parents: &mut HashMap<usize, usize>,
|
||||
views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
|
||||
refreshing: bool,
|
||||
cx: &mut WindowContext,
|
||||
) -> Result<Vector2F>;
|
||||
fn layout(&mut self, constraint: SizeConstraint, cx: &mut WindowContext) -> Result<Vector2F>;
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
origin: Vector2F,
|
||||
visible_bounds: RectF,
|
||||
cx: &mut WindowContext,
|
||||
|
@ -671,32 +642,16 @@ pub trait AnyRootElement {
|
|||
}
|
||||
|
||||
impl<V: View> AnyRootElement for RootElement<V> {
|
||||
fn layout(
|
||||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
new_parents: &mut HashMap<usize, usize>,
|
||||
views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
|
||||
refreshing: bool,
|
||||
cx: &mut WindowContext,
|
||||
) -> Result<Vector2F> {
|
||||
fn layout(&mut self, constraint: SizeConstraint, cx: &mut WindowContext) -> Result<Vector2F> {
|
||||
let view = self
|
||||
.view
|
||||
.upgrade(cx)
|
||||
.ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
|
||||
view.update(cx, |view, cx| {
|
||||
let mut cx = LayoutContext::new(
|
||||
cx,
|
||||
new_parents,
|
||||
views_to_notify_if_ancestors_change,
|
||||
refreshing,
|
||||
);
|
||||
Ok(self.element.layout(constraint, view, &mut cx))
|
||||
})
|
||||
view.update(cx, |view, cx| Ok(self.element.layout(constraint, view, cx)))
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
origin: Vector2F,
|
||||
visible_bounds: RectF,
|
||||
cx: &mut WindowContext,
|
||||
|
@ -707,9 +662,7 @@ impl<V: View> AnyRootElement for RootElement<V> {
|
|||
.ok_or_else(|| anyhow!("paint called on a root element for a dropped view"))?;
|
||||
|
||||
view.update(cx, |view, cx| {
|
||||
let mut cx = PaintContext::new(cx);
|
||||
self.element
|
||||
.paint(scene, origin, visible_bounds, view, &mut cx);
|
||||
self.element.paint(origin, visible_bounds, view, cx);
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
ViewContext,
|
||||
json, AnyElement, Element, SizeConstraint, ViewContext,
|
||||
};
|
||||
use json::ToJson;
|
||||
|
||||
|
@ -49,7 +48,7 @@ impl<V: 'static> Element<V> for Align<V> {
|
|||
&mut self,
|
||||
mut constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let mut size = constraint.max;
|
||||
constraint.min = Vector2F::zero();
|
||||
|
@ -65,12 +64,11 @@ impl<V: 'static> Element<V> for Align<V> {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let my_center = bounds.size() / 2.;
|
||||
let my_target = my_center + my_center * self.alignment;
|
||||
|
@ -79,7 +77,6 @@ impl<V: 'static> Element<V> for Align<V> {
|
|||
let child_target = child_center + child_center * self.alignment;
|
||||
|
||||
self.child.paint(
|
||||
scene,
|
||||
bounds.origin() - (child_target - my_target),
|
||||
visible_bounds,
|
||||
view,
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::marker::PhantomData;
|
|||
use super::Element;
|
||||
use crate::{
|
||||
json::{self, json},
|
||||
PaintContext, SceneBuilder, ViewContext,
|
||||
ViewContext,
|
||||
};
|
||||
use json::ToJson;
|
||||
use pathfinder_geometry::{
|
||||
|
@ -15,7 +15,7 @@ pub struct Canvas<V, F>(F, PhantomData<V>);
|
|||
|
||||
impl<V, F> Canvas<V, F>
|
||||
where
|
||||
F: FnMut(&mut SceneBuilder, RectF, RectF, &mut V, &mut ViewContext<V>),
|
||||
F: FnMut(RectF, RectF, &mut V, &mut ViewContext<V>),
|
||||
{
|
||||
pub fn new(f: F) -> Self {
|
||||
Self(f, PhantomData)
|
||||
|
@ -24,7 +24,7 @@ where
|
|||
|
||||
impl<V: 'static, F> Element<V> for Canvas<V, F>
|
||||
where
|
||||
F: 'static + FnMut(&mut SceneBuilder, RectF, RectF, &mut V, &mut ViewContext<V>),
|
||||
F: 'static + FnMut(RectF, RectF, &mut V, &mut ViewContext<V>),
|
||||
{
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
@ -33,7 +33,7 @@ where
|
|||
&mut self,
|
||||
constraint: crate::SizeConstraint,
|
||||
_: &mut V,
|
||||
_: &mut crate::LayoutContext<V>,
|
||||
_: &mut crate::ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let x = if constraint.max.x().is_finite() {
|
||||
constraint.max.x()
|
||||
|
@ -50,14 +50,13 @@ where
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
self.0(scene, bounds, visible_bounds, view, cx)
|
||||
self.0(bounds, visible_bounds, view, cx)
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
|
|
@ -3,10 +3,7 @@ use std::ops::Range;
|
|||
use pathfinder_geometry::{rect::RectF, vector::Vector2F};
|
||||
use serde_json::json;
|
||||
|
||||
use crate::{
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
ViewContext,
|
||||
};
|
||||
use crate::{json, AnyElement, Element, SizeConstraint, ViewContext};
|
||||
|
||||
pub struct Clipped<V> {
|
||||
child: AnyElement<V>,
|
||||
|
@ -26,24 +23,23 @@ impl<V: 'static> Element<V> for Clipped<V> {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
(self.child.layout(constraint, view, cx), ())
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
scene.paint_layer(Some(bounds), |scene| {
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx)
|
||||
})
|
||||
cx.scene().push_layer(Some(bounds));
|
||||
let state = self.child.paint(bounds.origin(), visible_bounds, view, cx);
|
||||
cx.scene().pop_layer();
|
||||
state
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
|
|
@ -2,9 +2,7 @@ use std::{any::Any, marker::PhantomData};
|
|||
|
||||
use pathfinder_geometry::{rect::RectF, vector::Vector2F};
|
||||
|
||||
use crate::{
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
|
||||
};
|
||||
use crate::{AnyElement, Element, SizeConstraint, ViewContext};
|
||||
|
||||
use super::Empty;
|
||||
|
||||
|
@ -284,14 +282,14 @@ impl<V: 'static, C: StatefulComponent<V> + 'static> Element<V> for ComponentAdap
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<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());
|
||||
.render(view, cx);
|
||||
self.element = Some(element);
|
||||
}
|
||||
let constraint = self.element.as_mut().unwrap().layout(constraint, view, cx);
|
||||
|
@ -300,17 +298,16 @@ impl<V: 'static, C: StatefulComponent<V> + 'static> Element<V> for ComponentAdap
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
self.element
|
||||
.as_mut()
|
||||
.expect("Layout should always be called before paint")
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx)
|
||||
.paint(bounds.origin(), visible_bounds, view, cx)
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
|
|
@ -5,8 +5,7 @@ use serde_json::json;
|
|||
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
ViewContext,
|
||||
json, AnyElement, Element, SizeConstraint, ViewContext,
|
||||
};
|
||||
|
||||
pub struct ConstrainedBox<V> {
|
||||
|
@ -16,7 +15,7 @@ pub struct ConstrainedBox<V> {
|
|||
|
||||
pub enum Constraint<V> {
|
||||
Static(SizeConstraint),
|
||||
Dynamic(Box<dyn FnMut(SizeConstraint, &mut V, &mut LayoutContext<V>) -> SizeConstraint>),
|
||||
Dynamic(Box<dyn FnMut(SizeConstraint, &mut V, &mut ViewContext<V>) -> SizeConstraint>),
|
||||
}
|
||||
|
||||
impl<V> ToJson for Constraint<V> {
|
||||
|
@ -38,8 +37,7 @@ impl<V: 'static> ConstrainedBox<V> {
|
|||
|
||||
pub fn dynamically(
|
||||
mut self,
|
||||
constraint: impl 'static
|
||||
+ FnMut(SizeConstraint, &mut V, &mut LayoutContext<V>) -> SizeConstraint,
|
||||
constraint: impl 'static + FnMut(SizeConstraint, &mut V, &mut ViewContext<V>) -> SizeConstraint,
|
||||
) -> Self {
|
||||
self.constraint = Constraint::Dynamic(Box::new(constraint));
|
||||
self
|
||||
|
@ -121,7 +119,7 @@ impl<V: 'static> ConstrainedBox<V> {
|
|||
&mut self,
|
||||
input_constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> SizeConstraint {
|
||||
match &mut self.constraint {
|
||||
Constraint::Static(constraint) => *constraint,
|
||||
|
@ -140,7 +138,7 @@ impl<V: 'static> Element<V> for ConstrainedBox<V> {
|
|||
&mut self,
|
||||
mut parent_constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let constraint = self.constraint(parent_constraint, view, cx);
|
||||
parent_constraint.min = parent_constraint.min.max(constraint.min);
|
||||
|
@ -152,17 +150,15 @@ impl<V: 'static> Element<V> for ConstrainedBox<V> {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
scene.paint_layer(Some(visible_bounds), |scene| {
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
})
|
||||
cx.scene().push_layer(Some(visible_bounds));
|
||||
self.child.paint(bounds.origin(), visible_bounds, view, cx);
|
||||
cx.scene().pop_layer();
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
|
|
@ -9,8 +9,8 @@ use crate::{
|
|||
},
|
||||
json::ToJson,
|
||||
platform::CursorStyle,
|
||||
scene::{self, Border, CornerRadii, CursorRegion, Quad},
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
|
||||
scene::{self, CornerRadii, CursorRegion, Quad},
|
||||
AnyElement, Element, SizeConstraint, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
|
@ -206,6 +206,163 @@ impl<V> Container<V> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Default, JsonSchema)]
|
||||
pub struct Border {
|
||||
pub color: Color,
|
||||
pub width: f32,
|
||||
pub overlay: bool,
|
||||
pub top: bool,
|
||||
pub bottom: bool,
|
||||
pub left: bool,
|
||||
pub right: bool,
|
||||
}
|
||||
|
||||
impl Into<scene::Border> for Border {
|
||||
fn into(self) -> scene::Border {
|
||||
scene::Border {
|
||||
color: self.color,
|
||||
left: if self.left { self.width } else { 0.0 },
|
||||
right: if self.right { self.width } else { 0.0 },
|
||||
top: if self.top { self.width } else { 0.0 },
|
||||
bottom: if self.bottom { self.width } else { 0.0 },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Border {
|
||||
pub fn new(width: f32, color: Color) -> Self {
|
||||
Self {
|
||||
width,
|
||||
color,
|
||||
overlay: false,
|
||||
top: false,
|
||||
left: false,
|
||||
bottom: false,
|
||||
right: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all(width: f32, color: Color) -> Self {
|
||||
Self {
|
||||
width,
|
||||
color,
|
||||
overlay: false,
|
||||
top: true,
|
||||
left: true,
|
||||
bottom: true,
|
||||
right: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn top(width: f32, color: Color) -> Self {
|
||||
let mut border = Self::new(width, color);
|
||||
border.top = true;
|
||||
border
|
||||
}
|
||||
|
||||
pub fn left(width: f32, color: Color) -> Self {
|
||||
let mut border = Self::new(width, color);
|
||||
border.left = true;
|
||||
border
|
||||
}
|
||||
|
||||
pub fn bottom(width: f32, color: Color) -> Self {
|
||||
let mut border = Self::new(width, color);
|
||||
border.bottom = true;
|
||||
border
|
||||
}
|
||||
|
||||
pub fn right(width: f32, color: Color) -> Self {
|
||||
let mut border = Self::new(width, color);
|
||||
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 top_width(&self) -> f32 {
|
||||
if self.top {
|
||||
self.width
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn left_width(&self) -> f32 {
|
||||
if self.left {
|
||||
self.width
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Border {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct BorderData {
|
||||
pub width: f32,
|
||||
pub color: Color,
|
||||
#[serde(default)]
|
||||
pub overlay: bool,
|
||||
#[serde(default)]
|
||||
pub top: bool,
|
||||
#[serde(default)]
|
||||
pub right: bool,
|
||||
#[serde(default)]
|
||||
pub bottom: bool,
|
||||
#[serde(default)]
|
||||
pub left: bool,
|
||||
}
|
||||
|
||||
let data = BorderData::deserialize(deserializer)?;
|
||||
let mut border = Border {
|
||||
width: data.width,
|
||||
color: data.color,
|
||||
overlay: data.overlay,
|
||||
top: data.top,
|
||||
bottom: data.bottom,
|
||||
left: data.left,
|
||||
right: data.right,
|
||||
};
|
||||
if !border.top && !border.bottom && !border.left && !border.right {
|
||||
border.top = true;
|
||||
border.bottom = true;
|
||||
border.left = true;
|
||||
border.right = true;
|
||||
}
|
||||
Ok(border)
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for Border {
|
||||
fn to_json(&self) -> serde_json::Value {
|
||||
let mut value = json!({});
|
||||
if self.top {
|
||||
value["top"] = json!(self.width);
|
||||
}
|
||||
if self.right {
|
||||
value["right"] = json!(self.width);
|
||||
}
|
||||
if self.bottom {
|
||||
value["bottom"] = json!(self.width);
|
||||
}
|
||||
if self.left {
|
||||
value["left"] = json!(self.width);
|
||||
}
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static> Element<V> for Container<V> {
|
||||
type LayoutState = ();
|
||||
type PaintState = ();
|
||||
|
@ -214,7 +371,7 @@ impl<V: 'static> Element<V> for Container<V> {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let mut size_buffer = self.margin_size() + self.padding_size();
|
||||
if !self.style.border.overlay {
|
||||
|
@ -230,12 +387,11 @@ impl<V: 'static> Element<V> for Container<V> {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let quad_bounds = RectF::from_points(
|
||||
bounds.origin() + vec2f(self.style.margin.left, self.style.margin.top),
|
||||
|
@ -243,7 +399,7 @@ impl<V: 'static> Element<V> for Container<V> {
|
|||
);
|
||||
|
||||
if let Some(shadow) = self.style.shadow.as_ref() {
|
||||
scene.push_shadow(scene::Shadow {
|
||||
cx.scene().push_shadow(scene::Shadow {
|
||||
bounds: quad_bounds + shadow.offset,
|
||||
corner_radii: self.style.corner_radii,
|
||||
sigma: shadow.blur,
|
||||
|
@ -253,7 +409,7 @@ impl<V: 'static> Element<V> for Container<V> {
|
|||
|
||||
if let Some(hit_bounds) = quad_bounds.intersection(visible_bounds) {
|
||||
if let Some(style) = self.style.cursor {
|
||||
scene.push_cursor_region(CursorRegion {
|
||||
cx.scene().push_cursor_region(CursorRegion {
|
||||
bounds: hit_bounds,
|
||||
style,
|
||||
});
|
||||
|
@ -264,29 +420,28 @@ impl<V: 'static> Element<V> for Container<V> {
|
|||
quad_bounds.origin() + vec2f(self.style.padding.left, self.style.padding.top);
|
||||
|
||||
if self.style.border.overlay {
|
||||
scene.push_quad(Quad {
|
||||
cx.scene().push_quad(Quad {
|
||||
bounds: quad_bounds,
|
||||
background: self.style.background_color,
|
||||
border: Default::default(),
|
||||
corner_radii: self.style.corner_radii.into(),
|
||||
});
|
||||
|
||||
self.child
|
||||
.paint(scene, child_origin, visible_bounds, view, cx);
|
||||
self.child.paint(child_origin, visible_bounds, view, cx);
|
||||
|
||||
scene.push_layer(None);
|
||||
scene.push_quad(Quad {
|
||||
cx.scene().push_layer(None);
|
||||
cx.scene().push_quad(Quad {
|
||||
bounds: quad_bounds,
|
||||
background: self.style.overlay_color,
|
||||
border: self.style.border,
|
||||
border: self.style.border.into(),
|
||||
corner_radii: self.style.corner_radii.into(),
|
||||
});
|
||||
scene.pop_layer();
|
||||
cx.scene().pop_layer();
|
||||
} else {
|
||||
scene.push_quad(Quad {
|
||||
cx.scene().push_quad(Quad {
|
||||
bounds: quad_bounds,
|
||||
background: self.style.background_color,
|
||||
border: self.style.border,
|
||||
border: self.style.border.into(),
|
||||
corner_radii: self.style.corner_radii.into(),
|
||||
});
|
||||
|
||||
|
@ -295,18 +450,17 @@ impl<V: 'static> Element<V> for Container<V> {
|
|||
self.style.border.left_width(),
|
||||
self.style.border.top_width(),
|
||||
);
|
||||
self.child
|
||||
.paint(scene, child_origin, visible_bounds, view, cx);
|
||||
self.child.paint(child_origin, visible_bounds, view, cx);
|
||||
|
||||
if self.style.overlay_color.is_some() {
|
||||
scene.push_layer(None);
|
||||
scene.push_quad(Quad {
|
||||
cx.scene().push_layer(None);
|
||||
cx.scene().push_quad(Quad {
|
||||
bounds: quad_bounds,
|
||||
background: self.style.overlay_color,
|
||||
border: Default::default(),
|
||||
corner_radii: self.style.corner_radii.into(),
|
||||
});
|
||||
scene.pop_layer();
|
||||
cx.scene().pop_layer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{json, ToJson},
|
||||
LayoutContext, PaintContext, SceneBuilder, ViewContext,
|
||||
ViewContext,
|
||||
};
|
||||
use crate::{Element, SizeConstraint};
|
||||
|
||||
|
@ -34,7 +34,7 @@ impl<V: 'static> Element<V> for Empty {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
_: &mut V,
|
||||
_: &mut LayoutContext<V>,
|
||||
_: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let x = if constraint.max.x().is_finite() && !self.collapsed {
|
||||
constraint.max.x()
|
||||
|
@ -52,12 +52,11 @@ impl<V: 'static> Element<V> for Empty {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
_: &mut SceneBuilder,
|
||||
_: RectF,
|
||||
_: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
_: &mut PaintContext<V>,
|
||||
_: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
}
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@ use std::ops::Range;
|
|||
|
||||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json, AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
ViewContext,
|
||||
json, AnyElement, Element, SizeConstraint, ViewContext,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
|
@ -43,7 +42,7 @@ impl<V: 'static> Element<V> for Expanded<V> {
|
|||
&mut self,
|
||||
mut constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
if self.full_width {
|
||||
constraint.min.set_x(constraint.max.x());
|
||||
|
@ -57,15 +56,13 @@ impl<V: 'static> Element<V> for Expanded<V> {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
self.child.paint(bounds.origin(), visible_bounds, view, cx);
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
|
|
@ -2,8 +2,7 @@ use std::{any::Any, cell::Cell, f32::INFINITY, ops::Range, rc::Rc};
|
|||
|
||||
use crate::{
|
||||
json::{self, ToJson, Value},
|
||||
AnyElement, Axis, Element, ElementStateHandle, LayoutContext, PaintContext, SceneBuilder,
|
||||
SizeConstraint, Vector2FExt, ViewContext,
|
||||
AnyElement, Axis, Element, ElementStateHandle, SizeConstraint, Vector2FExt, ViewContext,
|
||||
};
|
||||
use pathfinder_geometry::{
|
||||
rect::RectF,
|
||||
|
@ -85,7 +84,7 @@ impl<V: 'static> Flex<V> {
|
|||
remaining_flex: &mut f32,
|
||||
cross_axis_max: &mut f32,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
let cross_axis = self.axis.invert();
|
||||
for child in self.children.iter_mut() {
|
||||
|
@ -136,7 +135,7 @@ impl<V: 'static> Element<V> for Flex<V> {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let mut total_flex = None;
|
||||
let mut fixed_space = self.children.len().saturating_sub(1) as f32 * self.spacing;
|
||||
|
@ -225,7 +224,7 @@ impl<V: 'static> Element<V> for Flex<V> {
|
|||
}
|
||||
|
||||
if let Some(scroll_state) = self.scroll_state.as_ref() {
|
||||
scroll_state.0.update(cx.view_context(), |scroll_state, _| {
|
||||
scroll_state.0.update(cx, |scroll_state, _| {
|
||||
if let Some(scroll_to) = scroll_state.scroll_to.take() {
|
||||
let visible_start = scroll_state.scroll_position.get();
|
||||
let visible_end = visible_start + size.along(self.axis);
|
||||
|
@ -260,26 +259,25 @@ impl<V: 'static> Element<V> for Flex<V> {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
remaining_space: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
|
||||
let mut remaining_space = *remaining_space;
|
||||
let overflowing = remaining_space < 0.;
|
||||
if overflowing {
|
||||
scene.push_layer(Some(visible_bounds));
|
||||
cx.scene().push_layer(Some(visible_bounds));
|
||||
}
|
||||
|
||||
if let Some(scroll_state) = &self.scroll_state {
|
||||
scene.push_mouse_region(
|
||||
crate::MouseRegion::new::<Self>(scroll_state.1, 0, bounds)
|
||||
if let Some((scroll_state, id)) = &self.scroll_state {
|
||||
let scroll_state = scroll_state.read(cx).clone();
|
||||
cx.scene().push_mouse_region(
|
||||
crate::MouseRegion::new::<Self>(*id, 0, bounds)
|
||||
.on_scroll({
|
||||
let scroll_state = scroll_state.0.read(cx).clone();
|
||||
let axis = self.axis;
|
||||
move |e, _: &mut V, cx| {
|
||||
if remaining_space < 0. {
|
||||
|
@ -358,7 +356,7 @@ impl<V: 'static> Element<V> for Flex<V> {
|
|||
aligned_child_origin
|
||||
};
|
||||
|
||||
child.paint(scene, aligned_child_origin, visible_bounds, view, cx);
|
||||
child.paint(aligned_child_origin, visible_bounds, view, cx);
|
||||
|
||||
match self.axis {
|
||||
Axis::Horizontal => child_origin += vec2f(child.size().x() + self.spacing, 0.0),
|
||||
|
@ -367,7 +365,7 @@ impl<V: 'static> Element<V> for Flex<V> {
|
|||
}
|
||||
|
||||
if overflowing {
|
||||
scene.pop_layer();
|
||||
cx.scene().pop_layer();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -443,7 +441,7 @@ impl<V: 'static> Element<V> for FlexItem<V> {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let size = self.child.layout(constraint, view, cx);
|
||||
(size, ())
|
||||
|
@ -451,15 +449,13 @@ impl<V: 'static> Element<V> for FlexItem<V> {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx)
|
||||
self.child.paint(bounds.origin(), visible_bounds, view, cx)
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::ops::Range;
|
|||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::json,
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
|
||||
AnyElement, Element, SizeConstraint, ViewContext,
|
||||
};
|
||||
|
||||
pub struct Hook<V> {
|
||||
|
@ -36,7 +36,7 @@ impl<V: 'static> Element<V> for Hook<V> {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let size = self.child.layout(constraint, view, cx);
|
||||
if let Some(handler) = self.after_layout.as_mut() {
|
||||
|
@ -47,15 +47,13 @@ impl<V: 'static> Element<V> for Hook<V> {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
self.child.paint(bounds.origin(), visible_bounds, view, cx);
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
|
|
@ -1,12 +1,11 @@
|
|||
use super::constrain_size_preserving_aspect_ratio;
|
||||
use super::{constrain_size_preserving_aspect_ratio, Border};
|
||||
use crate::{
|
||||
geometry::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::{json, ToJson},
|
||||
scene, Border, Element, ImageData, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
ViewContext,
|
||||
scene, Element, ImageData, SizeConstraint, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
|
@ -65,7 +64,7 @@ impl<V: 'static> Element<V> for Image {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
_: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let data = match &self.source {
|
||||
ImageSource::Path(path) => match cx.asset_cache.png(path) {
|
||||
|
@ -92,17 +91,16 @@ impl<V: 'static> Element<V> for Image {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
_: RectF,
|
||||
layout: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
_: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
if let Some(data) = layout {
|
||||
scene.push_image(scene::Image {
|
||||
cx.scene().push_image(scene::Image {
|
||||
bounds,
|
||||
border: self.style.border,
|
||||
border: self.style.border.into(),
|
||||
corner_radii: self.style.corner_radius.into(),
|
||||
grayscale: self.style.grayscale,
|
||||
data: data.clone(),
|
||||
|
|
|
@ -39,7 +39,7 @@ impl<V: 'static> Element<V> for KeystrokeLabel {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, AnyElement<V>) {
|
||||
let mut element = if let Some(keystrokes) =
|
||||
cx.keystrokes_for_action(self.view_id, self.action.as_ref())
|
||||
|
@ -61,14 +61,13 @@ impl<V: 'static> Element<V> for KeystrokeLabel {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
element: &mut AnyElement<V>,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
element.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
element.paint(bounds.origin(), visible_bounds, view, cx);
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
|||
},
|
||||
json::{ToJson, Value},
|
||||
text_layout::{Line, RunStyle},
|
||||
Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
|
||||
Element, SizeConstraint, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
|
@ -136,7 +136,7 @@ impl<V: 'static> Element<V> for Label {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
_: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let runs = self.compute_runs();
|
||||
let line = cx.text_layout_cache().layout_str(
|
||||
|
@ -158,21 +158,14 @@ impl<V: 'static> Element<V> for Label {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
line: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let visible_bounds = bounds.intersection(visible_bounds).unwrap_or_default();
|
||||
line.paint(
|
||||
scene,
|
||||
bounds.origin(),
|
||||
visible_bounds,
|
||||
bounds.size().y(),
|
||||
cx,
|
||||
)
|
||||
line.paint(bounds.origin(), visible_bounds, bounds.size().y(), cx)
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
|
|
@ -4,8 +4,7 @@ use crate::{
|
|||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
json::json,
|
||||
AnyElement, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder, SizeConstraint,
|
||||
ViewContext,
|
||||
AnyElement, Element, MouseRegion, SizeConstraint, ViewContext,
|
||||
};
|
||||
use std::{cell::RefCell, collections::VecDeque, fmt::Debug, ops::Range, rc::Rc};
|
||||
use sum_tree::{Bias, SumTree};
|
||||
|
@ -100,7 +99,7 @@ impl<V: 'static> Element<V> for List<V> {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let state = &mut *self.state.0.borrow_mut();
|
||||
let size = constraint.max;
|
||||
|
@ -108,7 +107,7 @@ impl<V: 'static> Element<V> for List<V> {
|
|||
item_constraint.min.set_y(0.);
|
||||
item_constraint.max.set_y(f32::INFINITY);
|
||||
|
||||
if cx.refreshing || state.last_layout_width != Some(size.x()) {
|
||||
if cx.refreshing() || state.last_layout_width != Some(size.x()) {
|
||||
state.rendered_range = 0..0;
|
||||
state.items = SumTree::from_iter(
|
||||
(0..state.items.summary().count).map(|_| ListItem::Unrendered),
|
||||
|
@ -250,17 +249,17 @@ impl<V: 'static> Element<V> for List<V> {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
scroll_top: &mut ListOffset,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
|
||||
scene.push_layer(Some(visible_bounds));
|
||||
scene.push_mouse_region(
|
||||
MouseRegion::new::<Self>(cx.view_id(), 0, bounds).on_scroll({
|
||||
cx.scene().push_layer(Some(visible_bounds));
|
||||
let view_id = cx.view_id();
|
||||
cx.scene()
|
||||
.push_mouse_region(MouseRegion::new::<Self>(view_id, 0, bounds).on_scroll({
|
||||
let state = self.state.clone();
|
||||
let height = bounds.height();
|
||||
let scroll_top = scroll_top.clone();
|
||||
|
@ -274,17 +273,14 @@ impl<V: 'static> Element<V> for List<V> {
|
|||
cx,
|
||||
)
|
||||
}
|
||||
}),
|
||||
);
|
||||
}));
|
||||
|
||||
let state = &mut *self.state.0.borrow_mut();
|
||||
for (element, origin) in state.visible_elements(bounds, scroll_top) {
|
||||
element
|
||||
.borrow_mut()
|
||||
.paint(scene, origin, visible_bounds, view, cx);
|
||||
element.borrow_mut().paint(origin, visible_bounds, view, cx);
|
||||
}
|
||||
|
||||
scene.pop_layer();
|
||||
cx.scene().pop_layer();
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
@ -453,7 +449,7 @@ impl<V: 'static> StateInner<V> {
|
|||
existing_element: Option<&ListItem<V>>,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Option<Rc<RefCell<AnyElement<V>>>> {
|
||||
if let Some(ListItem::Rendered(element)) = existing_element {
|
||||
Some(element.clone())
|
||||
|
@ -647,7 +643,7 @@ impl<'a> sum_tree::SeekTarget<'a, ListItemSummary, ListItemSummary> for Height {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::{elements::Empty, geometry::vector::vec2f, Entity, PaintContext};
|
||||
use crate::{elements::Empty, geometry::vector::vec2f, Entity};
|
||||
use rand::prelude::*;
|
||||
use std::env;
|
||||
|
||||
|
@ -666,15 +662,7 @@ mod tests {
|
|||
});
|
||||
|
||||
let mut list = List::new(state.clone());
|
||||
let mut new_parents = Default::default();
|
||||
let mut notify_views_if_parents_change = Default::default();
|
||||
let mut layout_cx = LayoutContext::new(
|
||||
cx,
|
||||
&mut new_parents,
|
||||
&mut notify_views_if_parents_change,
|
||||
false,
|
||||
);
|
||||
let (size, _) = list.layout(constraint, &mut view, &mut layout_cx);
|
||||
let (size, _) = list.layout(constraint, &mut view, cx);
|
||||
assert_eq!(size, vec2f(100., 40.));
|
||||
assert_eq!(
|
||||
state.0.borrow().items.summary().clone(),
|
||||
|
@ -698,13 +686,7 @@ mod tests {
|
|||
cx,
|
||||
);
|
||||
|
||||
let mut layout_cx = LayoutContext::new(
|
||||
cx,
|
||||
&mut new_parents,
|
||||
&mut notify_views_if_parents_change,
|
||||
false,
|
||||
);
|
||||
let (_, logical_scroll_top) = list.layout(constraint, &mut view, &mut layout_cx);
|
||||
let (_, logical_scroll_top) = list.layout(constraint, &mut view, cx);
|
||||
assert_eq!(
|
||||
logical_scroll_top,
|
||||
ListOffset {
|
||||
|
@ -728,13 +710,7 @@ mod tests {
|
|||
}
|
||||
);
|
||||
|
||||
let mut layout_cx = LayoutContext::new(
|
||||
cx,
|
||||
&mut new_parents,
|
||||
&mut notify_views_if_parents_change,
|
||||
false,
|
||||
);
|
||||
let (size, logical_scroll_top) = list.layout(constraint, &mut view, &mut layout_cx);
|
||||
let (size, logical_scroll_top) = list.layout(constraint, &mut view, cx);
|
||||
assert_eq!(size, vec2f(100., 40.));
|
||||
assert_eq!(
|
||||
state.0.borrow().items.summary().clone(),
|
||||
|
@ -852,18 +828,10 @@ mod tests {
|
|||
|
||||
let mut list = List::new(state.clone());
|
||||
let window_size = vec2f(width, height);
|
||||
let mut new_parents = Default::default();
|
||||
let mut notify_views_if_parents_change = Default::default();
|
||||
let mut layout_cx = LayoutContext::new(
|
||||
cx,
|
||||
&mut new_parents,
|
||||
&mut notify_views_if_parents_change,
|
||||
false,
|
||||
);
|
||||
let (size, logical_scroll_top) = list.layout(
|
||||
SizeConstraint::new(vec2f(0., 0.), window_size),
|
||||
&mut view,
|
||||
&mut layout_cx,
|
||||
cx,
|
||||
);
|
||||
assert_eq!(size, window_size);
|
||||
last_logical_scroll_top = Some(logical_scroll_top);
|
||||
|
@ -976,20 +944,12 @@ mod tests {
|
|||
&mut self,
|
||||
_: SizeConstraint,
|
||||
_: &mut V,
|
||||
_: &mut LayoutContext<V>,
|
||||
_: &mut ViewContext<V>,
|
||||
) -> (Vector2F, ()) {
|
||||
(self.size, ())
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
_: &mut SceneBuilder,
|
||||
_: RectF,
|
||||
_: RectF,
|
||||
_: &mut (),
|
||||
_: &mut V,
|
||||
_: &mut PaintContext<V>,
|
||||
) {
|
||||
fn paint(&mut self, _: RectF, _: RectF, _: &mut (), _: &mut V, _: &mut ViewContext<V>) {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@ use crate::{
|
|||
CursorRegion, HandlerSet, MouseClick, MouseClickOut, MouseDown, MouseDownOut, MouseDrag,
|
||||
MouseHover, MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
|
||||
},
|
||||
AnyElement, Element, EventContext, LayoutContext, MouseRegion, MouseState, PaintContext,
|
||||
SceneBuilder, SizeConstraint, TypeTag, ViewContext,
|
||||
AnyElement, Element, EventContext, MouseRegion, MouseState, SizeConstraint, TypeTag,
|
||||
ViewContext,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::ops::Range;
|
||||
|
@ -236,26 +236,21 @@ impl<V: 'static> MouseEventHandler<V> {
|
|||
.round_out()
|
||||
}
|
||||
|
||||
fn paint_regions(
|
||||
&self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
fn paint_regions(&self, bounds: RectF, visible_bounds: RectF, cx: &mut ViewContext<V>) {
|
||||
let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
|
||||
let hit_bounds = self.hit_bounds(visible_bounds);
|
||||
|
||||
if let Some(style) = self.cursor_style {
|
||||
scene.push_cursor_region(CursorRegion {
|
||||
cx.scene().push_cursor_region(CursorRegion {
|
||||
bounds: hit_bounds,
|
||||
style,
|
||||
});
|
||||
}
|
||||
scene.push_mouse_region(
|
||||
let view_id = cx.view_id();
|
||||
cx.scene().push_mouse_region(
|
||||
MouseRegion::from_handlers(
|
||||
self.tag,
|
||||
cx.view_id(),
|
||||
view_id,
|
||||
self.region_id,
|
||||
hit_bounds,
|
||||
self.handlers.clone(),
|
||||
|
@ -275,31 +270,27 @@ impl<V: 'static> Element<V> for MouseEventHandler<V> {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
(self.child.layout(constraint, view, cx), ())
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
if self.above {
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
|
||||
scene.paint_layer(None, |scene| {
|
||||
self.paint_regions(scene, bounds, visible_bounds, cx);
|
||||
self.child.paint(bounds.origin(), visible_bounds, view, cx);
|
||||
cx.paint_layer(None, |cx| {
|
||||
self.paint_regions(bounds, visible_bounds, cx);
|
||||
});
|
||||
} else {
|
||||
self.paint_regions(scene, bounds, visible_bounds, cx);
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
self.paint_regions(bounds, visible_bounds, cx);
|
||||
self.child.paint(bounds.origin(), visible_bounds, view, cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,7 @@ use std::ops::Range;
|
|||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::ToJson,
|
||||
AnyElement, Axis, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder,
|
||||
SizeConstraint, ViewContext,
|
||||
AnyElement, Axis, Element, MouseRegion, SizeConstraint, ViewContext,
|
||||
};
|
||||
use serde_json::json;
|
||||
|
||||
|
@ -125,7 +124,7 @@ impl<V: 'static> Element<V> for Overlay<V> {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let constraint = if self.anchor_position.is_some() {
|
||||
SizeConstraint::new(Vector2F::zero(), cx.window_size())
|
||||
|
@ -138,12 +137,11 @@ impl<V: 'static> Element<V> for Overlay<V> {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
_: RectF,
|
||||
size: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
let (anchor_position, mut bounds) = match self.position_mode {
|
||||
OverlayPositionMode::Window => {
|
||||
|
@ -213,25 +211,23 @@ impl<V: 'static> Element<V> for Overlay<V> {
|
|||
OverlayFitMode::None => {}
|
||||
}
|
||||
|
||||
scene.paint_stacking_context(None, self.z_index, |scene| {
|
||||
if self.hoverable {
|
||||
enum OverlayHoverCapture {}
|
||||
// Block hovers in lower stacking contexts
|
||||
scene.push_mouse_region(MouseRegion::new::<OverlayHoverCapture>(
|
||||
cx.view_id(),
|
||||
cx.view_id(),
|
||||
bounds,
|
||||
cx.scene().push_stacking_context(None, self.z_index);
|
||||
if self.hoverable {
|
||||
enum OverlayHoverCapture {}
|
||||
// Block hovers in lower stacking contexts
|
||||
let view_id = cx.view_id();
|
||||
cx.scene()
|
||||
.push_mouse_region(MouseRegion::new::<OverlayHoverCapture>(
|
||||
view_id, view_id, bounds,
|
||||
));
|
||||
}
|
||||
|
||||
self.child.paint(
|
||||
scene,
|
||||
bounds.origin(),
|
||||
RectF::new(Vector2F::zero(), cx.window_size()),
|
||||
view,
|
||||
cx,
|
||||
);
|
||||
});
|
||||
}
|
||||
self.child.paint(
|
||||
bounds.origin(),
|
||||
RectF::new(Vector2F::zero(), cx.window_size()),
|
||||
view,
|
||||
cx,
|
||||
);
|
||||
cx.scene().pop_stacking_context();
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
|
|
@ -7,8 +7,7 @@ use serde_json::json;
|
|||
use crate::{
|
||||
geometry::rect::RectF,
|
||||
platform::{CursorStyle, MouseButton},
|
||||
AnyElement, AppContext, Axis, Element, LayoutContext, MouseRegion, PaintContext, SceneBuilder,
|
||||
SizeConstraint, TypeTag, View, ViewContext,
|
||||
AnyElement, AppContext, Axis, Element, MouseRegion, SizeConstraint, TypeTag, View, ViewContext,
|
||||
};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
@ -105,77 +104,77 @@ impl<V: 'static> Element<V> for Resizable<V> {
|
|||
&mut self,
|
||||
constraint: crate::SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
(self.child.layout(constraint, view, cx), constraint)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: pathfinder_geometry::rect::RectF,
|
||||
visible_bounds: pathfinder_geometry::rect::RectF,
|
||||
constraint: &mut SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
scene.push_stacking_context(None, None);
|
||||
cx.scene().push_stacking_context(None, None);
|
||||
|
||||
let handle_region = self.handle_side.of_rect(bounds, self.handle_size);
|
||||
|
||||
enum ResizeHandle {}
|
||||
scene.push_mouse_region(
|
||||
MouseRegion::new::<ResizeHandle>(
|
||||
cx.view_id(),
|
||||
self.handle_side as usize,
|
||||
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);
|
||||
let view_id = cx.view_id();
|
||||
cx.scene().push_mouse_region(
|
||||
MouseRegion::new::<ResizeHandle>(view_id, self.handle_side as usize, 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;
|
||||
let prev_size = side.relevant_component(bounds.size());
|
||||
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;
|
||||
})
|
||||
.on_drag(MouseButton::Left, {
|
||||
let bounds = bounds.clone();
|
||||
let side = self.handle_side;
|
||||
let prev_size = side.relevant_component(bounds.size());
|
||||
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 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, Some(new_size), cx);
|
||||
}
|
||||
}
|
||||
|
||||
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, Some(new_size), cx);
|
||||
}
|
||||
}
|
||||
}),
|
||||
}),
|
||||
);
|
||||
|
||||
scene.push_cursor_region(crate::CursorRegion {
|
||||
cx.scene().push_cursor_region(crate::CursorRegion {
|
||||
bounds: handle_region,
|
||||
style: match self.handle_side.axis() {
|
||||
Axis::Horizontal => CursorStyle::ResizeLeftRight,
|
||||
|
@ -183,10 +182,9 @@ impl<V: 'static> Element<V> for Resizable<V> {
|
|||
},
|
||||
});
|
||||
|
||||
scene.pop_stacking_context();
|
||||
cx.scene().pop_stacking_context();
|
||||
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
self.child.paint(bounds.origin(), visible_bounds, view, cx);
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
@ -242,26 +240,24 @@ impl<V: View, P: 'static> Element<V> for BoundsProvider<V, P> {
|
|||
&mut self,
|
||||
constraint: crate::SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut crate::LayoutContext<V>,
|
||||
cx: &mut crate::ViewContext<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>,
|
||||
cx: &mut crate::ViewContext<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)
|
||||
self.child.paint(bounds.origin(), visible_bounds, view, cx)
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
|
|
@ -3,7 +3,7 @@ use std::ops::Range;
|
|||
use crate::{
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::{self, json, ToJson},
|
||||
AnyElement, Element, LayoutContext, PaintContext, SceneBuilder, SizeConstraint, ViewContext,
|
||||
AnyElement, Element, SizeConstraint, ViewContext,
|
||||
};
|
||||
|
||||
/// Element which renders it's children in a stack on top of each other.
|
||||
|
@ -34,7 +34,7 @@ impl<V: 'static> Element<V> for Stack<V> {
|
|||
&mut self,
|
||||
mut constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let mut size = constraint.min;
|
||||
let mut children = self.children.iter_mut();
|
||||
|
@ -52,17 +52,16 @@ impl<V: 'static> Element<V> for Stack<V> {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
for child in &mut self.children {
|
||||
scene.paint_layer(None, |scene| {
|
||||
child.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
});
|
||||
cx.scene().push_layer(None);
|
||||
child.paint(bounds.origin(), visible_bounds, view, cx);
|
||||
cx.scene().pop_layer();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
use super::constrain_size_preserving_aspect_ratio;
|
||||
use crate::json::ToJson;
|
||||
use crate::PaintContext;
|
||||
use crate::{
|
||||
color::Color,
|
||||
geometry::{
|
||||
rect::RectF,
|
||||
vector::{vec2f, Vector2F},
|
||||
},
|
||||
scene, Element, LayoutContext, SceneBuilder, SizeConstraint, ViewContext,
|
||||
scene, Element, SizeConstraint, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde_derive::Deserialize;
|
||||
|
@ -49,7 +48,7 @@ impl<V: 'static> Element<V> for Svg {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
_: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
match cx.asset_cache.svg(&self.path) {
|
||||
Ok(tree) => {
|
||||
|
@ -69,15 +68,14 @@ impl<V: 'static> Element<V> for Svg {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
_visible_bounds: RectF,
|
||||
svg: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
_: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
if let Some(svg) = svg.clone() {
|
||||
scene.push_icon(scene::Icon {
|
||||
cx.scene().push_icon(scene::Icon {
|
||||
bounds,
|
||||
svg,
|
||||
path: self.path.clone(),
|
||||
|
|
|
@ -7,8 +7,7 @@ use crate::{
|
|||
},
|
||||
json::{ToJson, Value},
|
||||
text_layout::{Line, RunStyle, ShapedBoundary},
|
||||
AppContext, Element, FontCache, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
TextLayoutCache, ViewContext,
|
||||
Element, FontCache, SizeConstraint, TextLayoutCache, ViewContext, WindowContext,
|
||||
};
|
||||
use log::warn;
|
||||
use serde_json::json;
|
||||
|
@ -21,7 +20,7 @@ pub struct Text {
|
|||
highlights: Option<Box<[(Range<usize>, HighlightStyle)]>>,
|
||||
custom_runs: Option<(
|
||||
Box<[Range<usize>]>,
|
||||
Box<dyn FnMut(usize, RectF, &mut SceneBuilder, &mut AppContext)>,
|
||||
Box<dyn FnMut(usize, RectF, &mut WindowContext)>,
|
||||
)>,
|
||||
}
|
||||
|
||||
|
@ -58,7 +57,7 @@ impl Text {
|
|||
pub fn with_custom_runs(
|
||||
mut self,
|
||||
runs: impl Into<Box<[Range<usize>]>>,
|
||||
callback: impl 'static + FnMut(usize, RectF, &mut SceneBuilder, &mut AppContext),
|
||||
callback: impl 'static + FnMut(usize, RectF, &mut WindowContext),
|
||||
) -> Self {
|
||||
self.custom_runs = Some((runs.into(), Box::new(callback)));
|
||||
self
|
||||
|
@ -78,7 +77,7 @@ impl<V: 'static> Element<V> for Text {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
_: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
// Convert the string and highlight ranges into an iterator of highlighted chunks.
|
||||
|
||||
|
@ -166,16 +165,15 @@ impl<V: 'static> Element<V> for Text {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
layout: &mut Self::LayoutState,
|
||||
_: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let mut origin = bounds.origin();
|
||||
let empty = Vec::new();
|
||||
let mut callback = |_, _, _: &mut SceneBuilder, _: &mut AppContext| {};
|
||||
let mut callback = |_, _, _: &mut WindowContext| {};
|
||||
|
||||
let mouse_runs;
|
||||
let custom_run_callback;
|
||||
|
@ -202,7 +200,6 @@ impl<V: 'static> Element<V> for Text {
|
|||
if boundaries.intersects(visible_bounds) {
|
||||
if self.soft_wrap {
|
||||
line.paint_wrapped(
|
||||
scene,
|
||||
origin,
|
||||
visible_bounds,
|
||||
layout.line_height,
|
||||
|
@ -210,7 +207,7 @@ impl<V: 'static> Element<V> for Text {
|
|||
cx,
|
||||
);
|
||||
} else {
|
||||
line.paint(scene, origin, visible_bounds, layout.line_height, cx);
|
||||
line.paint(origin, visible_bounds, layout.line_height, cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -248,7 +245,7 @@ impl<V: 'static> Element<V> for Text {
|
|||
*run_origin,
|
||||
glyph_origin + vec2f(0., layout.line_height),
|
||||
);
|
||||
custom_run_callback(*run_ix, bounds, scene, cx);
|
||||
custom_run_callback(*run_ix, bounds, cx);
|
||||
*run_origin =
|
||||
vec2f(origin.x(), glyph_origin.y() + layout.line_height);
|
||||
}
|
||||
|
@ -264,7 +261,7 @@ impl<V: 'static> Element<V> for Text {
|
|||
run_origin,
|
||||
glyph_origin + vec2f(0., layout.line_height),
|
||||
);
|
||||
custom_run_callback(run_ix, bounds, scene, cx);
|
||||
custom_run_callback(run_ix, bounds, cx);
|
||||
custom_runs.next();
|
||||
}
|
||||
|
||||
|
@ -294,7 +291,7 @@ impl<V: 'static> Element<V> for Text {
|
|||
run_origin,
|
||||
line_end + vec2f(0., layout.line_height),
|
||||
);
|
||||
custom_run_callback(run_ix, bounds, scene, cx);
|
||||
custom_run_callback(run_ix, bounds, cx);
|
||||
if end_offset == run_end_offset {
|
||||
custom_runs.next();
|
||||
}
|
||||
|
@ -411,18 +408,10 @@ mod tests {
|
|||
let mut view = TestView;
|
||||
fonts::with_font_cache(cx.font_cache().clone(), || {
|
||||
let mut text = Text::new("Hello\r\n", Default::default()).with_soft_wrap(true);
|
||||
let mut new_parents = Default::default();
|
||||
let mut notify_views_if_parents_change = Default::default();
|
||||
let mut layout_cx = LayoutContext::new(
|
||||
cx,
|
||||
&mut new_parents,
|
||||
&mut notify_views_if_parents_change,
|
||||
false,
|
||||
);
|
||||
let (_, state) = text.layout(
|
||||
SizeConstraint::new(Default::default(), vec2f(f32::INFINITY, f32::INFINITY)),
|
||||
&mut view,
|
||||
&mut layout_cx,
|
||||
cx,
|
||||
);
|
||||
assert_eq!(state.shaped_lines.len(), 2);
|
||||
assert_eq!(state.wrap_boundaries.len(), 2);
|
||||
|
|
|
@ -6,8 +6,7 @@ use crate::{
|
|||
fonts::TextStyle,
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::json,
|
||||
Action, Axis, ElementStateHandle, LayoutContext, PaintContext, SceneBuilder, SizeConstraint,
|
||||
Task, TypeTag, ViewContext,
|
||||
Action, Axis, ElementStateHandle, SizeConstraint, Task, TypeTag, ViewContext,
|
||||
};
|
||||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
|
@ -189,7 +188,7 @@ impl<V: 'static> Element<V> for Tooltip<V> {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
let size = self.child.layout(constraint, view, cx);
|
||||
if let Some(tooltip) = self.tooltip.as_mut() {
|
||||
|
@ -204,17 +203,15 @@ impl<V: 'static> Element<V> for Tooltip<V> {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
_: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
self.child
|
||||
.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
self.child.paint(bounds.origin(), visible_bounds, view, cx);
|
||||
if let Some(tooltip) = self.tooltip.as_mut() {
|
||||
tooltip.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||
tooltip.paint(bounds.origin(), visible_bounds, view, cx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
|||
},
|
||||
json::{self, json},
|
||||
platform::ScrollWheelEvent,
|
||||
AnyElement, LayoutContext, MouseRegion, PaintContext, SceneBuilder, ViewContext,
|
||||
AnyElement, MouseRegion, ViewContext,
|
||||
};
|
||||
use json::ToJson;
|
||||
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
|
||||
|
@ -158,7 +158,7 @@ impl<V: 'static> Element<V> for UniformList<V> {
|
|||
&mut self,
|
||||
constraint: SizeConstraint,
|
||||
view: &mut V,
|
||||
cx: &mut LayoutContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> (Vector2F, Self::LayoutState) {
|
||||
if constraint.max.y().is_infinite() {
|
||||
unimplemented!(
|
||||
|
@ -272,18 +272,17 @@ impl<V: 'static> Element<V> for UniformList<V> {
|
|||
|
||||
fn paint(
|
||||
&mut self,
|
||||
scene: &mut SceneBuilder,
|
||||
bounds: RectF,
|
||||
visible_bounds: RectF,
|
||||
layout: &mut Self::LayoutState,
|
||||
view: &mut V,
|
||||
cx: &mut PaintContext<V>,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Self::PaintState {
|
||||
let visible_bounds = visible_bounds.intersection(bounds).unwrap_or_default();
|
||||
|
||||
scene.push_layer(Some(visible_bounds));
|
||||
cx.scene().push_layer(Some(visible_bounds));
|
||||
|
||||
scene.push_mouse_region(
|
||||
cx.scene().push_mouse_region(
|
||||
MouseRegion::new::<Self>(self.view_id, 0, visible_bounds).on_scroll({
|
||||
let scroll_max = layout.scroll_max;
|
||||
let state = self.state.clone();
|
||||
|
@ -312,11 +311,11 @@ impl<V: 'static> Element<V> for UniformList<V> {
|
|||
);
|
||||
|
||||
for item in &mut layout.items {
|
||||
item.paint(scene, item_origin, visible_bounds, view, cx);
|
||||
item.paint(item_origin, visible_bounds, view, cx);
|
||||
item_origin += vec2f(0.0, layout.item_height);
|
||||
}
|
||||
|
||||
scene.pop_layer();
|
||||
cx.scene().pop_layer();
|
||||
}
|
||||
|
||||
fn rect_for_text_range(
|
||||
|
|
|
@ -60,7 +60,7 @@ pub struct Features {
|
|||
pub zero: Option<bool>,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, JsonSchema, Refineable)]
|
||||
#[derive(Clone, Debug, JsonSchema)]
|
||||
pub struct TextStyle {
|
||||
pub color: Color,
|
||||
pub font_family_name: Arc<str>,
|
||||
|
@ -80,19 +80,78 @@ impl TextStyle {
|
|||
..Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn refine(self, refinement: TextStyleRefinement) -> TextStyle {
|
||||
TextStyle {
|
||||
color: refinement.color.unwrap_or(self.color),
|
||||
font_family_name: refinement
|
||||
.font_family_name
|
||||
.unwrap_or_else(|| self.font_family_name.clone()),
|
||||
font_family_id: refinement.font_family_id.unwrap_or(self.font_family_id),
|
||||
font_id: refinement.font_id.unwrap_or(self.font_id),
|
||||
font_size: refinement.font_size.unwrap_or(self.font_size),
|
||||
font_properties: refinement.font_properties.unwrap_or(self.font_properties),
|
||||
underline: refinement.underline.unwrap_or(self.underline),
|
||||
soft_wrap: refinement.soft_wrap.unwrap_or(self.soft_wrap),
|
||||
impl TextStyle {
|
||||
pub fn refine(
|
||||
&mut self,
|
||||
refinement: &TextStyleRefinement,
|
||||
font_cache: &FontCache,
|
||||
) -> Result<()> {
|
||||
if let Some(font_size) = refinement.font_size {
|
||||
self.font_size = font_size;
|
||||
}
|
||||
if let Some(color) = refinement.color {
|
||||
self.color = color;
|
||||
}
|
||||
if let Some(underline) = refinement.underline {
|
||||
self.underline = underline;
|
||||
}
|
||||
|
||||
let mut update_font_id = false;
|
||||
if let Some(font_family) = refinement.font_family.clone() {
|
||||
self.font_family_id = font_cache.load_family(&[&font_family], &Default::default())?;
|
||||
self.font_family_name = font_family;
|
||||
update_font_id = true;
|
||||
}
|
||||
if let Some(font_weight) = refinement.font_weight {
|
||||
self.font_properties.weight = font_weight;
|
||||
update_font_id = true;
|
||||
}
|
||||
if let Some(font_style) = refinement.font_style {
|
||||
self.font_properties.style = font_style;
|
||||
update_font_id = true;
|
||||
}
|
||||
|
||||
if update_font_id {
|
||||
self.font_id = font_cache.select_font(self.font_family_id, &self.font_properties)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Default)]
|
||||
pub struct TextStyleRefinement {
|
||||
pub color: Option<Color>,
|
||||
pub font_family: Option<Arc<str>>,
|
||||
pub font_size: Option<f32>,
|
||||
pub font_weight: Option<Weight>,
|
||||
pub font_style: Option<Style>,
|
||||
pub underline: Option<Underline>,
|
||||
}
|
||||
|
||||
impl Refineable for TextStyleRefinement {
|
||||
type Refinement = Self;
|
||||
|
||||
fn refine(&mut self, refinement: &Self::Refinement) {
|
||||
if refinement.color.is_some() {
|
||||
self.color = refinement.color;
|
||||
}
|
||||
if refinement.font_family.is_some() {
|
||||
self.font_family = refinement.font_family.clone();
|
||||
}
|
||||
if refinement.font_size.is_some() {
|
||||
self.font_size = refinement.font_size;
|
||||
}
|
||||
if refinement.font_weight.is_some() {
|
||||
self.font_weight = refinement.font_weight;
|
||||
}
|
||||
if refinement.font_style.is_some() {
|
||||
self.font_style = refinement.font_style;
|
||||
}
|
||||
if refinement.underline.is_some() {
|
||||
self.underline = refinement.underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
use std::fmt::Debug;
|
||||
|
||||
use super::scene::{Path, PathVertex};
|
||||
use crate::{color::Color, json::ToJson};
|
||||
pub use pathfinder_geometry::*;
|
||||
|
@ -133,13 +135,14 @@ impl ToJson for RectF {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Refineable)]
|
||||
pub struct Point<T: Clone + Default> {
|
||||
#[derive(Refineable, Debug)]
|
||||
#[refineable(debug)]
|
||||
pub struct Point<T: Clone + Default + Debug> {
|
||||
pub x: T,
|
||||
pub y: T,
|
||||
}
|
||||
|
||||
impl<T: Clone + Default> Clone for Point<T> {
|
||||
impl<T: Clone + Default + Debug> Clone for Point<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
x: self.x.clone(),
|
||||
|
@ -148,7 +151,7 @@ impl<T: Clone + Default> Clone for Point<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Default> Into<taffy::geometry::Point<T>> for Point<T> {
|
||||
impl<T: Clone + Default + Debug> Into<taffy::geometry::Point<T>> for Point<T> {
|
||||
fn into(self) -> taffy::geometry::Point<T> {
|
||||
taffy::geometry::Point {
|
||||
x: self.x,
|
||||
|
@ -157,13 +160,14 @@ impl<T: Clone + Default> Into<taffy::geometry::Point<T>> for Point<T> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Refineable)]
|
||||
pub struct Size<T: Clone + Default> {
|
||||
#[derive(Refineable, Clone, Debug)]
|
||||
#[refineable(debug)]
|
||||
pub struct Size<T: Clone + Default + Debug> {
|
||||
pub width: T,
|
||||
pub height: T,
|
||||
}
|
||||
|
||||
impl<S, T: Clone + Default> From<taffy::geometry::Size<S>> for Size<T>
|
||||
impl<S, T: Clone + Default + Debug> From<taffy::geometry::Size<S>> for Size<T>
|
||||
where
|
||||
S: Into<T>,
|
||||
{
|
||||
|
@ -175,7 +179,7 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
impl<S, T: Clone + Default> Into<taffy::geometry::Size<S>> for Size<T>
|
||||
impl<S, T: Clone + Default + Debug> Into<taffy::geometry::Size<S>> for Size<T>
|
||||
where
|
||||
T: Into<S>,
|
||||
{
|
||||
|
@ -222,34 +226,15 @@ impl Size<Length> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default, Refineable)]
|
||||
pub struct Edges<T: Clone + Default> {
|
||||
#[derive(Clone, Default, Refineable, Debug)]
|
||||
#[refineable(debug)]
|
||||
pub struct Edges<T: Clone + Default + Debug> {
|
||||
pub top: T,
|
||||
pub right: T,
|
||||
pub bottom: T,
|
||||
pub left: T,
|
||||
}
|
||||
|
||||
impl Edges<DefiniteLength> {
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
top: pixels(0.),
|
||||
right: pixels(0.),
|
||||
bottom: pixels(0.),
|
||||
left: pixels(0.),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Rect<taffy::style::LengthPercentage> {
|
||||
taffy::geometry::Rect {
|
||||
top: self.top.to_taffy(rem_size),
|
||||
right: self.right.to_taffy(rem_size),
|
||||
bottom: self.bottom.to_taffy(rem_size),
|
||||
left: self.left.to_taffy(rem_size),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Edges<Length> {
|
||||
pub fn auto() -> Self {
|
||||
Self {
|
||||
|
@ -282,12 +267,76 @@ impl Edges<Length> {
|
|||
}
|
||||
}
|
||||
|
||||
impl Edges<DefiniteLength> {
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
top: pixels(0.),
|
||||
right: pixels(0.),
|
||||
bottom: pixels(0.),
|
||||
left: pixels(0.),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Rect<taffy::style::LengthPercentage> {
|
||||
taffy::geometry::Rect {
|
||||
top: self.top.to_taffy(rem_size),
|
||||
right: self.right.to_taffy(rem_size),
|
||||
bottom: self.bottom.to_taffy(rem_size),
|
||||
left: self.left.to_taffy(rem_size),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Edges<AbsoluteLength> {
|
||||
pub fn zero() -> Self {
|
||||
Self {
|
||||
top: pixels(0.),
|
||||
right: pixels(0.),
|
||||
bottom: pixels(0.),
|
||||
left: pixels(0.),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_taffy(&self, rem_size: f32) -> taffy::geometry::Rect<taffy::style::LengthPercentage> {
|
||||
taffy::geometry::Rect {
|
||||
top: self.top.to_taffy(rem_size),
|
||||
right: self.right.to_taffy(rem_size),
|
||||
bottom: self.bottom.to_taffy(rem_size),
|
||||
left: self.left.to_taffy(rem_size),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_pixels(&self, rem_size: f32) -> Edges<f32> {
|
||||
Edges {
|
||||
top: self.top.to_pixels(rem_size),
|
||||
right: self.right.to_pixels(rem_size),
|
||||
bottom: self.bottom.to_pixels(rem_size),
|
||||
left: self.left.to_pixels(rem_size),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Edges<f32> {
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.top == 0.0 && self.right == 0.0 && self.bottom == 0.0 && self.left == 0.0
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum AbsoluteLength {
|
||||
Pixels(f32),
|
||||
Rems(f32),
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for AbsoluteLength {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
AbsoluteLength::Pixels(pixels) => write!(f, "{}px", pixels),
|
||||
AbsoluteLength::Rems(rems) => write!(f, "{}rems", rems),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl AbsoluteLength {
|
||||
pub fn to_pixels(&self, rem_size: f32) -> f32 {
|
||||
match self {
|
||||
|
@ -295,6 +344,13 @@ impl AbsoluteLength {
|
|||
AbsoluteLength::Rems(rems) => rems * rem_size,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_taffy(&self, rem_size: f32) -> taffy::style::LengthPercentage {
|
||||
match self {
|
||||
AbsoluteLength::Pixels(pixels) => taffy::style::LengthPercentage::Length(*pixels),
|
||||
AbsoluteLength::Rems(rems) => taffy::style::LengthPercentage::Length(rems * rem_size),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for AbsoluteLength {
|
||||
|
@ -307,7 +363,7 @@ impl Default for AbsoluteLength {
|
|||
#[derive(Clone, Copy)]
|
||||
pub enum DefiniteLength {
|
||||
Absolute(AbsoluteLength),
|
||||
Relative(f32), // Percent, from 0 to 100.
|
||||
Relative(f32), // 0. to 1.
|
||||
}
|
||||
|
||||
impl DefiniteLength {
|
||||
|
@ -326,6 +382,15 @@ impl DefiniteLength {
|
|||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for DefiniteLength {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
DefiniteLength::Absolute(length) => std::fmt::Debug::fmt(length, f),
|
||||
DefiniteLength::Relative(fract) => write!(f, "{}%", (fract * 100.0) as i32),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AbsoluteLength> for DefiniteLength {
|
||||
fn from(length: AbsoluteLength) -> Self {
|
||||
Self::Absolute(length)
|
||||
|
@ -345,6 +410,15 @@ pub enum Length {
|
|||
Auto,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Length {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Length::Definite(definite_length) => write!(f, "{:?}", definite_length),
|
||||
Length::Auto => write!(f, "auto"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn relative<T: From<DefiniteLength>>(fraction: f32) -> T {
|
||||
DefiniteLength::Relative(fraction).into()
|
||||
}
|
||||
|
@ -387,3 +461,9 @@ impl Default for Length {
|
|||
Self::Definite(DefiniteLength::default())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<()> for Length {
|
||||
fn from(_: ()) -> Self {
|
||||
Self::Definite(DefiniteLength::default())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
mod app;
|
||||
mod image_cache;
|
||||
pub use app::*;
|
||||
mod assets;
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
|
@ -8,6 +9,7 @@ pub mod elements;
|
|||
pub mod font_cache;
|
||||
mod image_data;
|
||||
pub use crate::image_data::ImageData;
|
||||
pub use taffy;
|
||||
pub mod views;
|
||||
pub use font_cache::FontCache;
|
||||
mod clipboard;
|
||||
|
@ -27,9 +29,9 @@ pub mod json;
|
|||
pub mod keymap_matcher;
|
||||
pub mod platform;
|
||||
pub use gpui_macros::{test, Element};
|
||||
pub use usvg;
|
||||
pub use window::{
|
||||
Axis, EngineLayout, LayoutEngine, LayoutId, RectFExt, SizeConstraint, Vector2FExt,
|
||||
WindowContext,
|
||||
Axis, Layout, LayoutEngine, LayoutId, RectFExt, SizeConstraint, Vector2FExt, WindowContext,
|
||||
};
|
||||
|
||||
pub use anyhow;
|
||||
|
|
99
crates/gpui/src/image_cache.rs
Normal file
99
crates/gpui/src/image_cache.rs
Normal file
|
@ -0,0 +1,99 @@
|
|||
use std::sync::Arc;
|
||||
|
||||
use crate::ImageData;
|
||||
use collections::HashMap;
|
||||
use futures::{
|
||||
future::{BoxFuture, Shared},
|
||||
AsyncReadExt, FutureExt,
|
||||
};
|
||||
use image::ImageError;
|
||||
use parking_lot::Mutex;
|
||||
use thiserror::Error;
|
||||
use util::{
|
||||
arc_cow::ArcCow,
|
||||
http::{self, HttpClient},
|
||||
};
|
||||
|
||||
#[derive(Debug, Error, Clone)]
|
||||
pub enum Error {
|
||||
#[error("http error: {0}")]
|
||||
Client(#[from] http::Error),
|
||||
#[error("IO error: {0}")]
|
||||
Io(Arc<std::io::Error>),
|
||||
#[error("unexpected http status: {status}, body: {body}")]
|
||||
BadStatus {
|
||||
status: http::StatusCode,
|
||||
body: String,
|
||||
},
|
||||
#[error("image error: {0}")]
|
||||
Image(Arc<ImageError>),
|
||||
}
|
||||
|
||||
impl From<std::io::Error> for Error {
|
||||
fn from(error: std::io::Error) -> Self {
|
||||
Error::Io(Arc::new(error))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<ImageError> for Error {
|
||||
fn from(error: ImageError) -> Self {
|
||||
Error::Image(Arc::new(error))
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ImageCache {
|
||||
client: Arc<dyn HttpClient>,
|
||||
images: Arc<Mutex<HashMap<ArcCow<'static, str>, FetchImageFuture>>>,
|
||||
}
|
||||
|
||||
type FetchImageFuture = Shared<BoxFuture<'static, Result<Arc<ImageData>, Error>>>;
|
||||
|
||||
impl ImageCache {
|
||||
pub fn new(client: Arc<dyn HttpClient>) -> Self {
|
||||
ImageCache {
|
||||
client,
|
||||
images: Default::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get(
|
||||
&self,
|
||||
uri: impl Into<ArcCow<'static, str>>,
|
||||
) -> Shared<BoxFuture<'static, Result<Arc<ImageData>, Error>>> {
|
||||
let uri = uri.into();
|
||||
let mut images = self.images.lock();
|
||||
|
||||
match images.get(uri.as_ref()) {
|
||||
Some(future) => future.clone(),
|
||||
None => {
|
||||
let client = self.client.clone();
|
||||
let future = {
|
||||
let uri = uri.clone();
|
||||
async move {
|
||||
let mut response = client.get(uri.as_ref(), ().into(), true).await?;
|
||||
let mut body = Vec::new();
|
||||
response.body_mut().read_to_end(&mut body).await?;
|
||||
|
||||
if !response.status().is_success() {
|
||||
return Err(Error::BadStatus {
|
||||
status: response.status(),
|
||||
body: String::from_utf8_lossy(&body).into_owned(),
|
||||
});
|
||||
}
|
||||
|
||||
let format = image::guess_format(&body)?;
|
||||
let image =
|
||||
image::load_from_memory_with_format(&body, format)?.into_bgra8();
|
||||
|
||||
Ok(ImageData::new(image))
|
||||
}
|
||||
}
|
||||
.boxed()
|
||||
.shared();
|
||||
|
||||
images.insert(uri, future.clone());
|
||||
future
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -146,6 +146,7 @@ pub trait Window {
|
|||
fn titlebar_height(&self) -> f32;
|
||||
fn appearance(&self) -> Appearance;
|
||||
fn screen(&self) -> Rc<dyn Screen>;
|
||||
fn mouse_position(&self) -> Vector2F;
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any;
|
||||
fn set_input_handler(&mut self, input_handler: Box<dyn InputHandler>);
|
||||
|
|
|
@ -37,6 +37,7 @@ use objc::{
|
|||
runtime::{Class, Object, Sel},
|
||||
sel, sel_impl,
|
||||
};
|
||||
|
||||
use postage::oneshot;
|
||||
use ptr::null_mut;
|
||||
use std::{
|
||||
|
|
|
@ -577,7 +577,6 @@ impl Renderer {
|
|||
};
|
||||
for (ix, quad) in quads.iter().enumerate() {
|
||||
let bounds = quad.bounds * scale_factor;
|
||||
let border_width = quad.border.width * scale_factor;
|
||||
let shader_quad = shaders::GPUIQuad {
|
||||
origin: bounds.origin().round().to_float2(),
|
||||
size: bounds.size().round().to_float2(),
|
||||
|
@ -585,10 +584,10 @@ impl Renderer {
|
|||
.background
|
||||
.unwrap_or_else(Color::transparent_black)
|
||||
.to_uchar4(),
|
||||
border_top: border_width * (quad.border.top as usize as f32),
|
||||
border_right: border_width * (quad.border.right as usize as f32),
|
||||
border_bottom: border_width * (quad.border.bottom as usize as f32),
|
||||
border_left: border_width * (quad.border.left as usize as f32),
|
||||
border_top: quad.border.top * scale_factor,
|
||||
border_right: quad.border.right * scale_factor,
|
||||
border_bottom: quad.border.bottom * scale_factor,
|
||||
border_left: quad.border.left * scale_factor,
|
||||
border_color: quad.border.color.to_uchar4(),
|
||||
corner_radius_top_left: quad.corner_radii.top_left * scale_factor,
|
||||
corner_radius_top_right: quad.corner_radii.top_right * scale_factor,
|
||||
|
@ -746,7 +745,6 @@ impl Renderer {
|
|||
let origin = image.bounds.origin() * scale_factor;
|
||||
let target_size = image.bounds.size() * scale_factor;
|
||||
let corner_radii = image.corner_radii * scale_factor;
|
||||
let border_width = image.border.width * scale_factor;
|
||||
let (alloc_id, atlas_bounds) = self.image_cache.render(&image.data);
|
||||
images_by_atlas
|
||||
.entry(alloc_id.atlas_id)
|
||||
|
@ -756,10 +754,10 @@ impl Renderer {
|
|||
target_size: target_size.to_float2(),
|
||||
source_size: atlas_bounds.size().to_float2(),
|
||||
atlas_origin: atlas_bounds.origin().to_float2(),
|
||||
border_top: border_width * (image.border.top as usize as f32),
|
||||
border_right: border_width * (image.border.right as usize as f32),
|
||||
border_bottom: border_width * (image.border.bottom as usize as f32),
|
||||
border_left: border_width * (image.border.left as usize as f32),
|
||||
border_top: image.border.top * scale_factor,
|
||||
border_right: image.border.right * scale_factor,
|
||||
border_bottom: image.border.bottom * scale_factor,
|
||||
border_left: image.border.left * scale_factor,
|
||||
border_color: image.border.color.to_uchar4(),
|
||||
corner_radius_top_left: corner_radii.top_left,
|
||||
corner_radius_top_right: corner_radii.top_right,
|
||||
|
|
|
@ -202,6 +202,10 @@ impl platform::Window for StatusItem {
|
|||
}
|
||||
}
|
||||
|
||||
fn mouse_position(&self) -> Vector2F {
|
||||
unimplemented!()
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
|
||||
self
|
||||
}
|
||||
|
|
|
@ -221,6 +221,14 @@ unsafe fn build_classes() {
|
|||
};
|
||||
}
|
||||
|
||||
pub fn convert_mouse_position(position: NSPoint, window_height: f32) -> Vector2F {
|
||||
vec2f(
|
||||
position.x as f32,
|
||||
// MacOS screen coordinates are relative to bottom left
|
||||
window_height - position.y as f32,
|
||||
)
|
||||
}
|
||||
|
||||
unsafe fn build_window_class(name: &'static str, superclass: &Class) -> *const Class {
|
||||
let mut decl = ClassDecl::new(name, superclass).unwrap();
|
||||
decl.add_ivar::<*mut c_void>(WINDOW_STATE_IVAR);
|
||||
|
@ -661,6 +669,16 @@ impl platform::Window for MacWindow {
|
|||
}
|
||||
}
|
||||
|
||||
fn mouse_position(&self) -> Vector2F {
|
||||
let position = unsafe {
|
||||
self.0
|
||||
.borrow()
|
||||
.native_window
|
||||
.mouseLocationOutsideOfEventStream()
|
||||
};
|
||||
convert_mouse_position(position, self.content_size().y())
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
@ -988,7 +1006,40 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
|
|||
.flatten()
|
||||
.is_some();
|
||||
if !is_composing {
|
||||
handled = callback(Event::KeyDown(event));
|
||||
// if the IME has changed the key, we'll first emit an event with the character
|
||||
// generated by the IME system; then fallback to the keystroke if that is not
|
||||
// handled.
|
||||
// cases that we have working:
|
||||
// - " on a brazillian layout by typing <quote><space>
|
||||
// - ctrl-` on a brazillian layout by typing <ctrl-`>
|
||||
// - $ on a czech QWERTY layout by typing <alt-4>
|
||||
// - 4 on a czech QWERTY layout by typing <shift-4>
|
||||
// - ctrl-4 on a czech QWERTY layout by typing <ctrl-alt-4> (or <ctrl-shift-4>)
|
||||
if ime_text.is_some() && ime_text.as_ref() != Some(&event.keystroke.key) {
|
||||
let event_with_ime_text = KeyDownEvent {
|
||||
is_held: false,
|
||||
keystroke: Keystroke {
|
||||
// we match ctrl because some use-cases need it.
|
||||
// we don't match alt because it's often used to generate the optional character
|
||||
// we don't match shift because we're not here with letters (usually)
|
||||
// we don't match cmd/fn because they don't seem to use IME
|
||||
ctrl: event.keystroke.ctrl,
|
||||
alt: false,
|
||||
shift: false,
|
||||
cmd: false,
|
||||
function: false,
|
||||
key: ime_text.clone().unwrap(),
|
||||
},
|
||||
};
|
||||
handled = callback(Event::KeyDown(event_with_ime_text));
|
||||
}
|
||||
if !handled {
|
||||
// empty key happens when you type a deadkey in input composition.
|
||||
// (e.g. on a brazillian keyboard typing quote is a deadkey)
|
||||
if !event.keystroke.key.is_empty() {
|
||||
handled = callback(Event::KeyDown(event));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !handled {
|
||||
|
|
|
@ -332,6 +332,10 @@ impl super::Window for Window {
|
|||
Rc::new(Screen)
|
||||
}
|
||||
|
||||
fn mouse_position(&self) -> Vector2F {
|
||||
Vector2F::zero()
|
||||
}
|
||||
|
||||
fn as_any_mut(&mut self) -> &mut dyn Any {
|
||||
self
|
||||
}
|
||||
|
|
|
@ -8,7 +8,6 @@ use derive_more::Mul;
|
|||
use schemars::JsonSchema;
|
||||
use serde::Deserialize;
|
||||
use serde_derive::Serialize;
|
||||
use serde_json::json;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
borrow::Cow,
|
||||
|
@ -20,7 +19,6 @@ use crate::{
|
|||
color::Color,
|
||||
fonts::{FontId, GlyphId},
|
||||
geometry::{rect::RectF, vector::Vector2F},
|
||||
json::ToJson,
|
||||
platform::{current::Surface, CursorStyle},
|
||||
ImageData, WindowContext,
|
||||
};
|
||||
|
@ -28,10 +26,9 @@ pub use mouse_event::*;
|
|||
pub use mouse_region::*;
|
||||
|
||||
pub struct SceneBuilder {
|
||||
scale_factor: f32,
|
||||
stacking_contexts: Vec<StackingContext>,
|
||||
active_stacking_context_stack: Vec<usize>,
|
||||
/// Used by the playground crate.
|
||||
/// Used by the gpui2 crate.
|
||||
pub event_handlers: Vec<EventHandler>,
|
||||
#[cfg(debug_assertions)]
|
||||
mouse_region_ids: HashSet<MouseRegionId>,
|
||||
|
@ -171,15 +168,13 @@ pub struct Icon {
|
|||
pub color: Color,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, Debug, JsonSchema)]
|
||||
#[derive(Clone, Copy, Default, Debug)]
|
||||
pub struct Border {
|
||||
pub width: f32,
|
||||
pub color: Color,
|
||||
pub overlay: bool,
|
||||
pub top: bool,
|
||||
pub right: bool,
|
||||
pub bottom: bool,
|
||||
pub left: bool,
|
||||
pub top: f32,
|
||||
pub right: f32,
|
||||
pub bottom: f32,
|
||||
pub left: f32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, Debug)]
|
||||
|
@ -191,47 +186,6 @@ pub struct Underline {
|
|||
pub squiggly: bool,
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for Border {
|
||||
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
|
||||
where
|
||||
D: serde::Deserializer<'de>,
|
||||
{
|
||||
#[derive(Deserialize)]
|
||||
struct BorderData {
|
||||
pub width: f32,
|
||||
pub color: Color,
|
||||
#[serde(default)]
|
||||
pub overlay: bool,
|
||||
#[serde(default)]
|
||||
pub top: bool,
|
||||
#[serde(default)]
|
||||
pub right: bool,
|
||||
#[serde(default)]
|
||||
pub bottom: bool,
|
||||
#[serde(default)]
|
||||
pub left: bool,
|
||||
}
|
||||
|
||||
let data = BorderData::deserialize(deserializer)?;
|
||||
let mut border = Border {
|
||||
width: data.width,
|
||||
color: data.color,
|
||||
overlay: data.overlay,
|
||||
top: data.top,
|
||||
bottom: data.bottom,
|
||||
left: data.left,
|
||||
right: data.right,
|
||||
};
|
||||
if !border.top && !border.bottom && !border.left && !border.right {
|
||||
border.top = true;
|
||||
border.bottom = true;
|
||||
border.left = true;
|
||||
border.right = true;
|
||||
}
|
||||
Ok(border)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Path {
|
||||
pub bounds: RectF,
|
||||
|
@ -290,45 +244,40 @@ impl Scene {
|
|||
}
|
||||
|
||||
impl SceneBuilder {
|
||||
pub fn new(scale_factor: f32) -> Self {
|
||||
let stacking_context = StackingContext::new(None, 0);
|
||||
SceneBuilder {
|
||||
scale_factor,
|
||||
stacking_contexts: vec![stacking_context],
|
||||
active_stacking_context_stack: vec![0],
|
||||
pub fn new() -> Self {
|
||||
let mut this = SceneBuilder {
|
||||
stacking_contexts: Vec::new(),
|
||||
active_stacking_context_stack: Vec::new(),
|
||||
#[cfg(debug_assertions)]
|
||||
mouse_region_ids: Default::default(),
|
||||
mouse_region_ids: HashSet::default(),
|
||||
event_handlers: Vec::new(),
|
||||
}
|
||||
};
|
||||
this.clear();
|
||||
this
|
||||
}
|
||||
|
||||
pub fn build(mut self) -> Scene {
|
||||
self.stacking_contexts
|
||||
.sort_by_key(|context| context.z_index);
|
||||
pub fn clear(&mut self) {
|
||||
self.stacking_contexts.clear();
|
||||
self.stacking_contexts.push(StackingContext::new(None, 0));
|
||||
self.active_stacking_context_stack.clear();
|
||||
self.active_stacking_context_stack.push(0);
|
||||
#[cfg(debug_assertions)]
|
||||
self.mouse_region_ids.clear();
|
||||
}
|
||||
|
||||
pub fn build(&mut self, scale_factor: f32) -> Scene {
|
||||
let mut stacking_contexts = std::mem::take(&mut self.stacking_contexts);
|
||||
stacking_contexts.sort_by_key(|context| context.z_index);
|
||||
let event_handlers = std::mem::take(&mut self.event_handlers);
|
||||
self.clear();
|
||||
|
||||
Scene {
|
||||
scale_factor: self.scale_factor,
|
||||
stacking_contexts: self.stacking_contexts,
|
||||
event_handlers: self.event_handlers,
|
||||
scale_factor,
|
||||
stacking_contexts,
|
||||
event_handlers,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn scale_factor(&self) -> f32 {
|
||||
self.scale_factor
|
||||
}
|
||||
|
||||
pub fn paint_stacking_context<F>(
|
||||
&mut self,
|
||||
clip_bounds: Option<RectF>,
|
||||
z_index: Option<usize>,
|
||||
f: F,
|
||||
) where
|
||||
F: FnOnce(&mut Self),
|
||||
{
|
||||
self.push_stacking_context(clip_bounds, z_index);
|
||||
f(self);
|
||||
self.pop_stacking_context();
|
||||
}
|
||||
|
||||
pub fn push_stacking_context(&mut self, clip_bounds: Option<RectF>, z_index: Option<usize>) {
|
||||
let z_index = z_index.unwrap_or_else(|| self.active_stacking_context().z_index + 1);
|
||||
self.active_stacking_context_stack
|
||||
|
@ -342,15 +291,6 @@ impl SceneBuilder {
|
|||
assert!(!self.active_stacking_context_stack.is_empty());
|
||||
}
|
||||
|
||||
pub fn paint_layer<F>(&mut self, clip_bounds: Option<RectF>, f: F)
|
||||
where
|
||||
F: FnOnce(&mut Self),
|
||||
{
|
||||
self.push_layer(clip_bounds);
|
||||
f(self);
|
||||
self.pop_layer();
|
||||
}
|
||||
|
||||
pub fn push_layer(&mut self, clip_bounds: Option<RectF>) {
|
||||
self.active_stacking_context().push_layer(clip_bounds);
|
||||
}
|
||||
|
@ -606,99 +546,6 @@ impl Layer {
|
|||
}
|
||||
}
|
||||
|
||||
impl Border {
|
||||
pub fn new(width: f32, color: Color) -> Self {
|
||||
Self {
|
||||
width,
|
||||
color,
|
||||
overlay: false,
|
||||
top: false,
|
||||
left: false,
|
||||
bottom: false,
|
||||
right: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn all(width: f32, color: Color) -> Self {
|
||||
Self {
|
||||
width,
|
||||
color,
|
||||
overlay: false,
|
||||
top: true,
|
||||
left: true,
|
||||
bottom: true,
|
||||
right: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn top(width: f32, color: Color) -> Self {
|
||||
let mut border = Self::new(width, color);
|
||||
border.top = true;
|
||||
border
|
||||
}
|
||||
|
||||
pub fn left(width: f32, color: Color) -> Self {
|
||||
let mut border = Self::new(width, color);
|
||||
border.left = true;
|
||||
border
|
||||
}
|
||||
|
||||
pub fn bottom(width: f32, color: Color) -> Self {
|
||||
let mut border = Self::new(width, color);
|
||||
border.bottom = true;
|
||||
border
|
||||
}
|
||||
|
||||
pub fn right(width: f32, color: Color) -> Self {
|
||||
let mut border = Self::new(width, color);
|
||||
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 top_width(&self) -> f32 {
|
||||
if self.top {
|
||||
self.width
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
|
||||
pub fn left_width(&self) -> f32 {
|
||||
if self.left {
|
||||
self.width
|
||||
} else {
|
||||
0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ToJson for Border {
|
||||
fn to_json(&self) -> serde_json::Value {
|
||||
let mut value = json!({});
|
||||
if self.top {
|
||||
value["top"] = json!(self.width);
|
||||
}
|
||||
if self.right {
|
||||
value["right"] = json!(self.width);
|
||||
}
|
||||
if self.bottom {
|
||||
value["bottom"] = json!(self.width);
|
||||
}
|
||||
if self.left {
|
||||
value["left"] = json!(self.width);
|
||||
}
|
||||
value
|
||||
}
|
||||
}
|
||||
|
||||
impl MouseRegion {
|
||||
pub fn id(&self) -> MouseRegionId {
|
||||
self.id
|
||||
|
|
|
@ -9,7 +9,6 @@ use crate::{
|
|||
platform::FontSystem,
|
||||
scene,
|
||||
window::WindowContext,
|
||||
SceneBuilder,
|
||||
};
|
||||
use ordered_float::OrderedFloat;
|
||||
use parking_lot::{Mutex, RwLock, RwLockUpgradableReadGuard};
|
||||
|
@ -284,7 +283,6 @@ impl Line {
|
|||
|
||||
pub fn paint(
|
||||
&self,
|
||||
scene: &mut SceneBuilder,
|
||||
origin: Vector2F,
|
||||
visible_bounds: RectF,
|
||||
line_height: f32,
|
||||
|
@ -347,7 +345,7 @@ impl Line {
|
|||
}
|
||||
|
||||
if let Some((underline_origin, underline_style)) = finished_underline {
|
||||
scene.push_underline(scene::Underline {
|
||||
cx.scene().push_underline(scene::Underline {
|
||||
origin: underline_origin,
|
||||
width: glyph_origin.x() - underline_origin.x(),
|
||||
thickness: underline_style.thickness.into(),
|
||||
|
@ -357,14 +355,14 @@ impl Line {
|
|||
}
|
||||
|
||||
if glyph.is_emoji {
|
||||
scene.push_image_glyph(scene::ImageGlyph {
|
||||
cx.scene().push_image_glyph(scene::ImageGlyph {
|
||||
font_id: run.font_id,
|
||||
font_size: self.layout.font_size,
|
||||
id: glyph.id,
|
||||
origin: glyph_origin,
|
||||
});
|
||||
} else {
|
||||
scene.push_glyph(scene::Glyph {
|
||||
cx.scene().push_glyph(scene::Glyph {
|
||||
font_id: run.font_id,
|
||||
font_size: self.layout.font_size,
|
||||
id: glyph.id,
|
||||
|
@ -377,7 +375,7 @@ impl Line {
|
|||
|
||||
if let Some((underline_start, underline_style)) = underline.take() {
|
||||
let line_end_x = origin.x() + self.layout.width;
|
||||
scene.push_underline(scene::Underline {
|
||||
cx.scene().push_underline(scene::Underline {
|
||||
origin: underline_start,
|
||||
width: line_end_x - underline_start.x(),
|
||||
color: underline_style.color.unwrap(),
|
||||
|
@ -389,7 +387,6 @@ impl Line {
|
|||
|
||||
pub fn paint_wrapped(
|
||||
&self,
|
||||
scene: &mut SceneBuilder,
|
||||
origin: Vector2F,
|
||||
visible_bounds: RectF,
|
||||
line_height: f32,
|
||||
|
@ -417,7 +414,7 @@ impl Line {
|
|||
{
|
||||
boundaries.next();
|
||||
if let Some((underline_origin, underline_style)) = underline {
|
||||
scene.push_underline(scene::Underline {
|
||||
cx.scene().push_underline(scene::Underline {
|
||||
origin: underline_origin,
|
||||
width: glyph_origin.x() - underline_origin.x(),
|
||||
thickness: underline_style.thickness.into(),
|
||||
|
@ -461,7 +458,7 @@ impl Line {
|
|||
}
|
||||
|
||||
if let Some((underline_origin, underline_style)) = finished_underline {
|
||||
scene.push_underline(scene::Underline {
|
||||
cx.scene().push_underline(scene::Underline {
|
||||
origin: underline_origin,
|
||||
width: glyph_origin.x() - underline_origin.x(),
|
||||
thickness: underline_style.thickness.into(),
|
||||
|
@ -477,14 +474,14 @@ impl Line {
|
|||
);
|
||||
if glyph_bounds.intersects(visible_bounds) {
|
||||
if glyph.is_emoji {
|
||||
scene.push_image_glyph(scene::ImageGlyph {
|
||||
cx.scene().push_image_glyph(scene::ImageGlyph {
|
||||
font_id: run.font_id,
|
||||
font_size: self.layout.font_size,
|
||||
id: glyph.id,
|
||||
origin: glyph_bounds.origin() + baseline_offset,
|
||||
});
|
||||
} else {
|
||||
scene.push_glyph(scene::Glyph {
|
||||
cx.scene().push_glyph(scene::Glyph {
|
||||
font_id: run.font_id,
|
||||
font_size: self.layout.font_size,
|
||||
id: glyph.id,
|
||||
|
@ -498,7 +495,7 @@ impl Line {
|
|||
|
||||
if let Some((underline_origin, underline_style)) = underline.take() {
|
||||
let line_end_x = glyph_origin.x() + self.layout.width - prev_position;
|
||||
scene.push_underline(scene::Underline {
|
||||
cx.scene().push_underline(scene::Underline {
|
||||
origin: underline_origin,
|
||||
width: line_end_x - underline_origin.x(),
|
||||
thickness: underline_style.thickness.into(),
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue