Merge remote-tracking branch 'origin/main' into chat-again

This commit is contained in:
Nathan Sobo 2023-09-11 12:08:01 -06:00
commit fe6f0a253b
164 changed files with 7012 additions and 3586 deletions

View file

@ -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![]),
],
);

View file

@ -57,6 +57,7 @@ impl TestAppContext {
platform,
foreground_platform.clone(),
font_cache,
util::http::FakeHttpClient::with_404_response(),
RefCounts::new(leak_detector),
(),
);

View file

@ -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(&current_view_id);
let new_parent_id = new_parents.get(&current_view_id);
let new_parent_id = self.window.new_parents.get(&current_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 {

View file

@ -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(())
})
}

View file

@ -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,

View file

@ -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(

View file

@ -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(

View file

@ -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(

View file

@ -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(

View file

@ -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();
}
}
}

View file

@ -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 {
}

View file

@ -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(

View file

@ -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(

View file

@ -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(

View file

@ -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(),

View file

@ -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(

View file

@ -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(

View file

@ -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!()
}

View file

@ -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);
}
}

View file

@ -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(

View file

@ -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(

View file

@ -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();
}
}

View file

@ -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(),

View file

@ -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);

View file

@ -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);
}
}

View file

@ -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(

View file

@ -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;
}
}
}

View file

@ -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())
}
}

View file

@ -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;

View 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
}
}
}
}

View file

@ -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>);

View file

@ -37,6 +37,7 @@ use objc::{
runtime::{Class, Object, Sel},
sel, sel_impl,
};
use postage::oneshot;
use ptr::null_mut;
use std::{

View file

@ -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,

View file

@ -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
}

View file

@ -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 {

View file

@ -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
}

View file

@ -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

View file

@ -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(),