2024-01-22 16:51:21 +00:00
/// KeyDispatch is where GPUI deals with binding actions to key events.
///
/// The key pieces to making a key binding work are to define an action,
2024-01-22 16:56:59 +00:00
/// implement a method that takes that action as a type parameter,
2024-01-22 16:51:21 +00:00
/// and then to register the action during render on a focused node
/// with a keymap context:
///
/// ```rust
/// actions!(editor,[Undo, Redo]);;
///
/// impl Editor {
/// fn undo(&mut self, _: &Undo, _cx: &mut ViewContext<Self>) { ... }
/// fn redo(&mut self, _: &Redo, _cx: &mut ViewContext<Self>) { ... }
/// }
///
/// impl Render for Editor {
/// fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
/// div()
/// .track_focus(&self.focus_handle)
/// .keymap_context("Editor")
/// .on_action(cx.listener(Editor::undo))
/// .on_action(cx.listener(Editor::redo))
/// ...
/// }
/// }
///```
///
/// The keybindings themselves are managed independently by calling cx.bind_keys().
/// (Though mostly when developing Zed itself, you just need to add a new line to
/// assets/keymaps/default.json).
///
/// ```rust
/// cx.bind_keys([
/// KeyBinding::new("cmd-z", Editor::undo, Some("Editor")),
/// KeyBinding::new("cmd-shift-z", Editor::redo, Some("Editor")),
/// ])
/// ```
///
/// With all of this in place, GPUI will ensure that if you have an Editor that contains
/// the focus, hitting cmd-z will Undo.
///
/// In real apps, it is a little more complicated than this, because typically you have
/// several nested views that each register keyboard handlers. In this case action matching
/// bubbles up from the bottom. For example in Zed, the Workspace is the top-level view, which contains Pane's, which contain Editors. If there are conflicting keybindings defined
/// then the Editor's bindings take precedence over the Pane's bindings, which take precedence over the Workspace.
///
/// In GPUI, keybindings are not limited to just single keystrokes, you can define
/// sequences by separating the keys with a space:
///
/// KeyBinding::new("cmd-k left", pane::SplitLeft, Some("Pane"))
///
2023-11-10 21:47:45 +00:00
use crate ::{
2024-04-25 10:54:39 +00:00
Action , ActionRegistry , DispatchPhase , EntityId , FocusId , KeyBinding , KeyContext , Keymap ,
2024-07-22 16:46:16 +00:00
Keystroke , ModifiersChangedEvent , WindowContext ,
2023-11-10 21:47:45 +00:00
} ;
2024-02-07 12:16:22 +00:00
use collections ::FxHashMap ;
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
use smallvec ::SmallVec ;
2023-11-10 21:47:45 +00:00
use std ::{
any ::{ Any , TypeId } ,
2024-01-21 23:35:47 +00:00
cell ::RefCell ,
2024-01-09 11:37:24 +00:00
mem ,
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
ops ::Range ,
2023-11-13 17:29:18 +00:00
rc ::Rc ,
2023-11-10 21:47:45 +00:00
} ;
#[ derive(Clone, Copy, Debug, Eq, PartialEq, Hash) ]
2024-01-21 22:26:45 +00:00
pub ( crate ) struct DispatchNodeId ( usize ) ;
2023-11-10 21:47:45 +00:00
2023-11-13 17:29:18 +00:00
pub ( crate ) struct DispatchTree {
2023-11-10 21:47:45 +00:00
node_stack : Vec < DispatchNodeId > ,
2023-12-04 21:28:37 +00:00
pub ( crate ) context_stack : Vec < KeyContext > ,
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
view_stack : Vec < EntityId > ,
2023-11-10 21:47:45 +00:00
nodes : Vec < DispatchNode > ,
2024-02-07 12:16:22 +00:00
focusable_node_ids : FxHashMap < FocusId , DispatchNodeId > ,
view_node_ids : FxHashMap < EntityId , DispatchNodeId > ,
2024-01-21 23:35:47 +00:00
keymap : Rc < RefCell < Keymap > > ,
2023-11-17 01:32:02 +00:00
action_registry : Rc < ActionRegistry > ,
2023-11-10 21:47:45 +00:00
}
#[ derive(Default) ]
2023-11-13 17:29:18 +00:00
pub ( crate ) struct DispatchNode {
2023-12-12 12:16:09 +00:00
pub key_listeners : Vec < KeyListener > ,
pub action_listeners : Vec < DispatchActionListener > ,
2024-03-21 00:43:31 +00:00
pub modifiers_changed_listeners : Vec < ModifiersChangedListener > ,
2023-12-06 17:02:45 +00:00
pub context : Option < KeyContext > ,
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
pub focus_id : Option < FocusId > ,
2024-01-09 11:37:24 +00:00
view_id : Option < EntityId > ,
2023-11-10 21:47:45 +00:00
parent : Option < DispatchNodeId > ,
}
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
pub ( crate ) struct ReusedSubtree {
old_range : Range < usize > ,
new_range : Range < usize > ,
2024-07-19 15:04:18 +00:00
contains_focus : bool ,
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
}
impl ReusedSubtree {
pub fn refresh_node_id ( & self , node_id : DispatchNodeId ) -> DispatchNodeId {
debug_assert! (
self . old_range . contains ( & node_id . 0 ) ,
" node {} was not part of the reused subtree {:?} " ,
node_id . 0 ,
self . old_range
) ;
DispatchNodeId ( ( node_id . 0 - self . old_range . start ) + self . new_range . start )
}
2024-07-19 15:04:18 +00:00
pub fn contains_focus ( & self ) -> bool {
self . contains_focus
}
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
}
2024-07-22 16:46:16 +00:00
#[ derive(Default, Debug) ]
pub ( crate ) struct Replay {
pub ( crate ) keystroke : Keystroke ,
pub ( crate ) bindings : SmallVec < [ KeyBinding ; 1 ] > ,
}
#[ derive(Default, Debug) ]
pub ( crate ) struct DispatchResult {
pub ( crate ) pending : SmallVec < [ Keystroke ; 1 ] > ,
pub ( crate ) bindings : SmallVec < [ KeyBinding ; 1 ] > ,
pub ( crate ) to_replay : SmallVec < [ Replay ; 1 ] > ,
}
2024-04-25 10:54:39 +00:00
type KeyListener = Rc < dyn Fn ( & dyn Any , DispatchPhase , & mut WindowContext ) > ;
type ModifiersChangedListener = Rc < dyn Fn ( & ModifiersChangedEvent , & mut WindowContext ) > ;
2023-11-13 17:29:18 +00:00
2023-12-21 08:41:48 +00:00
#[ derive(Clone) ]
2023-11-13 19:48:36 +00:00
pub ( crate ) struct DispatchActionListener {
2023-11-13 17:29:18 +00:00
pub ( crate ) action_type : TypeId ,
2024-01-09 11:37:24 +00:00
pub ( crate ) listener : Rc < dyn Fn ( & dyn Any , DispatchPhase , & mut WindowContext ) > ,
2023-11-10 21:47:45 +00:00
}
2023-11-13 17:29:18 +00:00
impl DispatchTree {
2024-01-21 23:35:47 +00:00
pub fn new ( keymap : Rc < RefCell < Keymap > > , action_registry : Rc < ActionRegistry > ) -> Self {
2023-11-10 21:47:45 +00:00
Self {
node_stack : Vec ::new ( ) ,
context_stack : Vec ::new ( ) ,
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
view_stack : Vec ::new ( ) ,
2023-11-10 21:47:45 +00:00
nodes : Vec ::new ( ) ,
2024-02-07 12:16:22 +00:00
focusable_node_ids : FxHashMap ::default ( ) ,
view_node_ids : FxHashMap ::default ( ) ,
2023-11-10 21:47:45 +00:00
keymap ,
2023-11-17 01:32:02 +00:00
action_registry ,
2023-11-10 21:47:45 +00:00
}
}
pub fn clear ( & mut self ) {
self . node_stack . clear ( ) ;
2023-11-13 14:31:35 +00:00
self . context_stack . clear ( ) ;
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
self . view_stack . clear ( ) ;
2024-01-09 14:12:23 +00:00
self . nodes . clear ( ) ;
2023-11-13 14:31:35 +00:00
self . focusable_node_ids . clear ( ) ;
2024-01-09 11:37:24 +00:00
self . view_node_ids . clear ( ) ;
2023-11-10 21:47:45 +00:00
}
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
pub fn len ( & self ) -> usize {
self . nodes . len ( )
}
pub fn push_node ( & mut self ) -> DispatchNodeId {
2023-11-10 21:47:45 +00:00
let parent = self . node_stack . last ( ) . copied ( ) ;
let node_id = DispatchNodeId ( self . nodes . len ( ) ) ;
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
2023-11-10 21:47:45 +00:00
self . nodes . push ( DispatchNode {
parent ,
.. Default ::default ( )
} ) ;
self . node_stack . push ( node_id ) ;
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
node_id
}
2024-01-09 16:34:57 +00:00
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
pub fn set_active_node ( & mut self , node_id : DispatchNodeId ) {
let next_node_parent = self . nodes [ node_id . 0 ] . parent ;
while self . node_stack . last ( ) . copied ( ) ! = next_node_parent & & ! self . node_stack . is_empty ( ) {
self . pop_node ( ) ;
2023-11-10 21:47:45 +00:00
}
2024-01-09 16:34:57 +00:00
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
if self . node_stack . last ( ) . copied ( ) = = next_node_parent {
self . node_stack . push ( node_id ) ;
let active_node = & self . nodes [ node_id . 0 ] ;
if let Some ( view_id ) = active_node . view_id {
self . view_stack . push ( view_id )
}
if let Some ( context ) = active_node . context . clone ( ) {
self . context_stack . push ( context ) ;
}
} else {
debug_assert_eq! ( self . node_stack . len ( ) , 0 ) ;
let mut current_node_id = Some ( node_id ) ;
while let Some ( node_id ) = current_node_id {
let node = & self . nodes [ node_id . 0 ] ;
if let Some ( context ) = node . context . clone ( ) {
self . context_stack . push ( context ) ;
}
if node . view_id . is_some ( ) {
self . view_stack . push ( node . view_id . unwrap ( ) ) ;
}
self . node_stack . push ( node_id ) ;
current_node_id = node . parent ;
}
self . context_stack . reverse ( ) ;
self . view_stack . reverse ( ) ;
self . node_stack . reverse ( ) ;
2024-01-09 16:34:57 +00:00
}
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
}
pub fn set_key_context ( & mut self , context : KeyContext ) {
self . active_node ( ) . context = Some ( context . clone ( ) ) ;
self . context_stack . push ( context ) ;
}
pub fn set_focus_id ( & mut self , focus_id : FocusId ) {
let node_id = * self . node_stack . last ( ) . unwrap ( ) ;
self . nodes [ node_id . 0 ] . focus_id = Some ( focus_id ) ;
self . focusable_node_ids . insert ( focus_id , node_id ) ;
}
2024-01-09 16:34:57 +00:00
2024-03-19 17:16:18 +00:00
pub fn parent_view_id ( & mut self ) -> Option < EntityId > {
self . view_stack . last ( ) . copied ( )
}
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
pub fn set_view_id ( & mut self , view_id : EntityId ) {
if self . view_stack . last ( ) . copied ( ) ! = Some ( view_id ) {
let node_id = * self . node_stack . last ( ) . unwrap ( ) ;
self . nodes [ node_id . 0 ] . view_id = Some ( view_id ) ;
2024-01-09 16:34:57 +00:00
self . view_node_ids . insert ( view_id , node_id ) ;
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
self . view_stack . push ( view_id ) ;
2024-01-09 16:34:57 +00:00
}
2023-11-10 21:47:45 +00:00
}
pub fn pop_node ( & mut self ) {
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
let node = & self . nodes [ self . active_node_id ( ) . unwrap ( ) . 0 ] ;
2024-01-09 14:12:23 +00:00
if node . context . is_some ( ) {
2023-11-10 21:47:45 +00:00
self . context_stack . pop ( ) ;
}
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
if node . view_id . is_some ( ) {
self . view_stack . pop ( ) ;
}
2024-01-09 14:12:23 +00:00
self . node_stack . pop ( ) ;
2023-11-10 21:47:45 +00:00
}
2024-01-10 14:00:40 +00:00
fn move_node ( & mut self , source : & mut DispatchNode ) {
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
self . push_node ( ) ;
if let Some ( context ) = source . context . clone ( ) {
self . set_key_context ( context ) ;
}
if let Some ( focus_id ) = source . focus_id {
self . set_focus_id ( focus_id ) ;
}
if let Some ( view_id ) = source . view_id {
self . set_view_id ( view_id ) ;
}
2024-01-10 14:00:40 +00:00
let target = self . active_node ( ) ;
target . key_listeners = mem ::take ( & mut source . key_listeners ) ;
target . action_listeners = mem ::take ( & mut source . action_listeners ) ;
2024-03-21 00:43:31 +00:00
target . modifiers_changed_listeners = mem ::take ( & mut source . modifiers_changed_listeners ) ;
2024-01-09 11:37:24 +00:00
}
2024-07-19 15:04:18 +00:00
pub fn reuse_subtree (
& mut self ,
old_range : Range < usize > ,
source : & mut Self ,
focus : Option < FocusId > ,
) -> ReusedSubtree {
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
let new_range = self . nodes . len ( ) .. self . nodes . len ( ) + old_range . len ( ) ;
2024-01-09 11:37:24 +00:00
2024-07-19 15:04:18 +00:00
let mut contains_focus = false ;
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
let mut source_stack = vec! [ ] ;
2024-01-09 11:37:24 +00:00
for ( source_node_id , source_node ) in source
. nodes
. iter_mut ( )
. enumerate ( )
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
. skip ( old_range . start )
. take ( old_range . len ( ) )
2024-01-09 11:37:24 +00:00
{
let source_node_id = DispatchNodeId ( source_node_id ) ;
while let Some ( source_ancestor ) = source_stack . last ( ) {
2024-04-11 07:48:06 +00:00
if source_node . parent = = Some ( * source_ancestor ) {
break ;
} else {
2024-01-09 11:37:24 +00:00
source_stack . pop ( ) ;
self . pop_node ( ) ;
}
}
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
source_stack . push ( source_node_id ) ;
2024-07-19 15:04:18 +00:00
if source_node . focus_id . is_some ( ) & & source_node . focus_id = = focus {
contains_focus = true ;
}
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
self . move_node ( source_node ) ;
2024-01-09 11:37:24 +00:00
}
while ! source_stack . is_empty ( ) {
2024-01-10 09:50:16 +00:00
source_stack . pop ( ) ;
2024-01-09 11:37:24 +00:00
self . pop_node ( ) ;
}
2024-01-09 14:12:23 +00:00
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
ReusedSubtree {
old_range ,
new_range ,
2024-07-19 15:04:18 +00:00
contains_focus ,
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
}
2024-01-09 11:37:24 +00:00
}
2024-04-23 13:14:22 +00:00
pub fn truncate ( & mut self , index : usize ) {
for node in & self . nodes [ index .. ] {
if let Some ( focus_id ) = node . focus_id {
self . focusable_node_ids . remove ( & focus_id ) ;
}
if let Some ( view_id ) = node . view_id {
self . view_node_ids . remove ( & view_id ) ;
}
}
self . nodes . truncate ( index ) ;
}
2023-11-10 21:47:45 +00:00
pub fn on_key_event ( & mut self , listener : KeyListener ) {
self . active_node ( ) . key_listeners . push ( listener ) ;
}
2024-03-21 00:43:31 +00:00
pub fn on_modifiers_changed ( & mut self , listener : ModifiersChangedListener ) {
self . active_node ( )
. modifiers_changed_listeners
. push ( listener ) ;
}
2023-11-10 21:47:45 +00:00
pub fn on_action (
& mut self ,
action_type : TypeId ,
2024-01-09 11:37:24 +00:00
listener : Rc < dyn Fn ( & dyn Any , DispatchPhase , & mut WindowContext ) > ,
2023-11-10 21:47:45 +00:00
) {
2023-11-13 19:48:36 +00:00
self . active_node ( )
. action_listeners
. push ( DispatchActionListener {
action_type ,
listener ,
} ) ;
2023-11-10 21:47:45 +00:00
}
pub fn focus_contains ( & self , parent : FocusId , child : FocusId ) -> bool {
if parent = = child {
return true ;
}
if let Some ( parent_node_id ) = self . focusable_node_ids . get ( & parent ) {
let mut current_node_id = self . focusable_node_ids . get ( & child ) . copied ( ) ;
while let Some ( node_id ) = current_node_id {
if node_id = = * parent_node_id {
return true ;
}
current_node_id = self . nodes [ node_id . 0 ] . parent ;
}
}
false
}
2023-12-06 15:15:53 +00:00
pub fn available_actions ( & self , target : DispatchNodeId ) -> Vec < Box < dyn Action > > {
2023-12-08 21:02:14 +00:00
let mut actions = Vec ::< Box < dyn Action > > ::new ( ) ;
2023-12-06 15:15:53 +00:00
for node_id in self . dispatch_path ( target ) {
let node = & self . nodes [ node_id . 0 ] ;
for DispatchActionListener { action_type , .. } in & node . action_listeners {
2023-12-08 21:02:14 +00:00
if let Err ( ix ) = actions . binary_search_by_key ( action_type , | a | a . as_any ( ) . type_id ( ) )
{
// Intentionally silence these errors without logging.
// If an action cannot be built by default, it's not available.
let action = self . action_registry . build_action_type ( action_type ) . ok ( ) ;
if let Some ( action ) = action {
actions . insert ( ix , action ) ;
}
}
2023-11-10 21:47:45 +00:00
}
}
actions
}
2023-12-06 15:52:52 +00:00
pub fn is_action_available ( & self , action : & dyn Action , target : DispatchNodeId ) -> bool {
for node_id in self . dispatch_path ( target ) {
let node = & self . nodes [ node_id . 0 ] ;
if node
. action_listeners
. iter ( )
. any ( | listener | listener . action_type = = action . as_any ( ) . type_id ( ) )
{
return true ;
2023-12-05 20:17:59 +00:00
}
}
false
}
2023-12-04 21:28:37 +00:00
pub fn bindings_for_action (
& self ,
action : & dyn Action ,
2024-02-09 08:45:39 +00:00
context_stack : & [ KeyContext ] ,
2023-12-04 21:28:37 +00:00
) -> Vec < KeyBinding > {
2024-01-21 23:35:47 +00:00
let keymap = self . keymap . borrow ( ) ;
2024-01-05 20:07:20 +00:00
keymap
. bindings_for_action ( action )
. filter ( | binding | {
2024-07-22 16:46:16 +00:00
let ( bindings , _ ) = keymap . bindings_for_input ( & binding . keystrokes , & context_stack ) ;
bindings
. iter ( )
. next ( )
. is_some_and ( | b | b . action . partial_eq ( action ) )
2023-12-04 20:53:38 +00:00
} )
2023-11-13 22:33:22 +00:00
. cloned ( )
. collect ( )
}
2024-07-22 16:46:16 +00:00
fn bindings_for_input (
& self ,
input : & [ Keystroke ] ,
2024-01-20 21:11:10 +00:00
dispatch_path : & SmallVec < [ DispatchNodeId ; 32 ] > ,
2024-07-22 16:46:16 +00:00
) -> ( SmallVec < [ KeyBinding ; 1 ] > , bool ) {
let context_stack : SmallVec < [ KeyContext ; 4 ] > = dispatch_path
. iter ( )
. filter_map ( | node_id | self . node ( * node_id ) . context . clone ( ) )
. collect ( ) ;
2024-01-20 21:11:10 +00:00
2024-07-22 16:46:16 +00:00
self . keymap
. borrow ( )
. bindings_for_input ( & input , & context_stack )
}
2023-11-10 21:47:45 +00:00
2024-07-22 16:46:16 +00:00
/// dispatch_key processes the keystroke
/// input should be set to the value of `pending` from the previous call to dispatch_key.
/// This returns three instructions to the input handler:
/// - bindings: any bindings to execute before processing this keystroke
/// - pending: the new set of pending keystrokes to store
/// - to_replay: any keystroke that had been pushed to pending, but are no-longer matched,
/// these should be replayed first.
pub fn dispatch_key (
& mut self ,
mut input : SmallVec < [ Keystroke ; 1 ] > ,
keystroke : Keystroke ,
dispatch_path : & SmallVec < [ DispatchNodeId ; 32 ] > ,
) -> DispatchResult {
input . push ( keystroke . clone ( ) ) ;
let ( bindings , pending ) = self . bindings_for_input ( & input , dispatch_path ) ;
if pending {
return DispatchResult {
pending : input ,
.. Default ::default ( )
} ;
} else if ! bindings . is_empty ( ) {
return DispatchResult {
bindings ,
.. Default ::default ( )
} ;
} else if input . len ( ) = = 1 {
return DispatchResult ::default ( ) ;
2024-01-20 21:11:10 +00:00
}
2024-07-22 16:46:16 +00:00
input . pop ( ) ;
2023-11-10 21:47:45 +00:00
2024-07-22 16:46:16 +00:00
let ( suffix , mut to_replay ) = self . replay_prefix ( input , dispatch_path ) ;
2024-01-20 21:11:10 +00:00
2024-07-22 16:46:16 +00:00
let mut result = self . dispatch_key ( suffix , keystroke , dispatch_path ) ;
to_replay . extend ( result . to_replay ) ;
result . to_replay = to_replay ;
return result ;
}
2024-02-05 17:55:27 +00:00
2024-07-22 16:46:16 +00:00
/// If the user types a matching prefix of a binding and then waits for a timeout
/// flush_dispatch() converts any previously pending input to replay events.
pub fn flush_dispatch (
& mut self ,
input : SmallVec < [ Keystroke ; 1 ] > ,
dispatch_path : & SmallVec < [ DispatchNodeId ; 32 ] > ,
) -> SmallVec < [ Replay ; 1 ] > {
let ( suffix , mut to_replay ) = self . replay_prefix ( input , dispatch_path ) ;
if suffix . len ( ) > 0 {
to_replay . extend ( self . flush_dispatch ( suffix , dispatch_path ) )
2023-11-10 21:47:45 +00:00
}
2024-01-20 21:11:10 +00:00
2024-07-22 16:46:16 +00:00
to_replay
2023-11-10 21:47:45 +00:00
}
2024-07-22 16:46:16 +00:00
/// Converts the longest prefix of input to a replay event and returns the rest.
fn replay_prefix (
& mut self ,
mut input : SmallVec < [ Keystroke ; 1 ] > ,
dispatch_path : & SmallVec < [ DispatchNodeId ; 32 ] > ,
) -> ( SmallVec < [ Keystroke ; 1 ] > , SmallVec < [ Replay ; 1 ] > ) {
let mut to_replay : SmallVec < [ Replay ; 1 ] > = Default ::default ( ) ;
for last in ( 0 .. input . len ( ) ) . rev ( ) {
let ( bindings , _ ) = self . bindings_for_input ( & input [ 0 ..= last ] , dispatch_path ) ;
if ! bindings . is_empty ( ) {
to_replay . push ( Replay {
keystroke : input . drain ( 0 ..= last ) . last ( ) . unwrap ( ) ,
bindings ,
} ) ;
break ;
}
}
if to_replay . is_empty ( ) {
to_replay . push ( Replay {
keystroke : input . remove ( 0 ) ,
.. Default ::default ( )
} ) ;
}
( input , to_replay )
2023-12-11 22:06:33 +00:00
}
2023-11-13 17:29:18 +00:00
pub fn dispatch_path ( & self , target : DispatchNodeId ) -> SmallVec < [ DispatchNodeId ; 32 ] > {
2023-11-10 21:47:45 +00:00
let mut dispatch_path : SmallVec < [ DispatchNodeId ; 32 ] > = SmallVec ::new ( ) ;
let mut current_node_id = Some ( target ) ;
while let Some ( node_id ) = current_node_id {
dispatch_path . push ( node_id ) ;
current_node_id = self . nodes [ node_id . 0 ] . parent ;
}
dispatch_path . reverse ( ) ; // Reverse the path so it goes from the root to the focused node.
dispatch_path
}
2023-11-13 17:29:18 +00:00
2023-12-13 17:16:39 +00:00
pub fn focus_path ( & self , focus_id : FocusId ) -> SmallVec < [ FocusId ; 8 ] > {
let mut focus_path : SmallVec < [ FocusId ; 8 ] > = SmallVec ::new ( ) ;
let mut current_node_id = self . focusable_node_ids . get ( & focus_id ) . copied ( ) ;
while let Some ( node_id ) = current_node_id {
let node = self . node ( node_id ) ;
if let Some ( focus_id ) = node . focus_id {
focus_path . push ( focus_id ) ;
}
current_node_id = node . parent ;
}
focus_path . reverse ( ) ; // Reverse the path so it goes from the root to the focused node.
focus_path
}
2024-01-09 14:12:23 +00:00
pub fn view_path ( & self , view_id : EntityId ) -> SmallVec < [ EntityId ; 8 ] > {
let mut view_path : SmallVec < [ EntityId ; 8 ] > = SmallVec ::new ( ) ;
let mut current_node_id = self . view_node_ids . get ( & view_id ) . copied ( ) ;
while let Some ( node_id ) = current_node_id {
let node = self . node ( node_id ) ;
if let Some ( view_id ) = node . view_id {
view_path . push ( view_id ) ;
}
current_node_id = node . parent ;
}
view_path . reverse ( ) ; // Reverse the path so it goes from the root to the view node.
view_path
}
2023-11-13 17:33:08 +00:00
pub fn node ( & self , node_id : DispatchNodeId ) -> & DispatchNode {
& self . nodes [ node_id . 0 ]
}
fn active_node ( & mut self ) -> & mut DispatchNode {
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
let active_node_id = self . active_node_id ( ) . unwrap ( ) ;
2023-11-13 17:33:08 +00:00
& mut self . nodes [ active_node_id . 0 ]
}
2023-11-13 17:29:18 +00:00
pub fn focusable_node_id ( & self , target : FocusId ) -> Option < DispatchNodeId > {
self . focusable_node_ids . get ( & target ) . copied ( )
}
2023-11-13 17:33:08 +00:00
2023-12-06 15:15:53 +00:00
pub fn root_node_id ( & self ) -> DispatchNodeId {
debug_assert! ( ! self . nodes . is_empty ( ) ) ;
DispatchNodeId ( 0 )
}
Fix flickering (#9012)
See https://zed.dev/channel/gpui-536
Fixes https://github.com/zed-industries/zed/issues/9010
Fixes https://github.com/zed-industries/zed/issues/8883
Fixes https://github.com/zed-industries/zed/issues/8640
Fixes https://github.com/zed-industries/zed/issues/8598
Fixes https://github.com/zed-industries/zed/issues/8579
Fixes https://github.com/zed-industries/zed/issues/8363
Fixes https://github.com/zed-industries/zed/issues/8207
### Problem
After transitioning Zed to GPUI 2, we started noticing that interacting
with the mouse on many UI elements would lead to a pretty annoying
flicker. The main issue with the old approach was that hover state was
calculated based on the previous frame. That is, when computing whether
a given element was hovered in the current frame, we would use
information about the same element in the previous frame.
However, inspecting the previous frame tells us very little about what
should be hovered in the current frame, as elements in the current frame
may have changed significantly.
### Solution
This pull request's main contribution is the introduction of a new
`after_layout` phase when redrawing the window. The key idea is that
we'll give every element a chance to register a hitbox (see
`ElementContext::insert_hitbox`) before painting anything. Then, during
the `paint` phase, elements can determine whether they're the topmost
and draw their hover state accordingly.
We are also removing the ability to give an arbitrary z-index to
elements. Instead, we will follow the much simpler painter's algorithm.
That is, an element that gets painted after will be drawn on top of an
element that got painted earlier. Elements can still escape their
current "stacking context" by using the new `ElementContext::defer_draw`
method (see `Overlay` for an example). Elements drawn using this method
will still be logically considered as being children of their original
parent (for keybinding, focus and cache invalidation purposes) but their
layout and paint passes will be deferred until the currently-drawn
element is done.
With these changes we also reworked geometry batching within the
`Scene`. The new approach uses an AABB tree to determine geometry
occlusion, which allows the GPU to render non-overlapping geometry in
parallel.
### Performance
Performance is slightly better than on `main` even though this new
approach is more correct and we're maintaining an extra data structure
(the AABB tree).
![before_after](https://github.com/zed-industries/zed/assets/482957/c8120b07-1dbd-4776-834a-d040e569a71e)
Release Notes:
- Fixed a bug that was causing popovers to flicker.
---------
Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Thorsten <thorsten@zed.dev>
2024-03-11 09:45:57 +00:00
pub fn active_node_id ( & self ) -> Option < DispatchNodeId > {
self . node_stack . last ( ) . copied ( )
2023-11-13 17:33:08 +00:00
}
2023-11-10 21:47:45 +00:00
}
2024-01-09 02:21:54 +00:00
#[ cfg(test) ]
mod tests {
2024-01-21 23:35:47 +00:00
use std ::{ cell ::RefCell , rc ::Rc } ;
2024-01-09 02:21:54 +00:00
use crate ::{ Action , ActionRegistry , DispatchTree , KeyBinding , KeyContext , Keymap } ;
#[ derive(PartialEq, Eq) ]
struct TestAction ;
impl Action for TestAction {
fn name ( & self ) -> & 'static str {
" test::TestAction "
}
fn debug_name ( ) -> & 'static str
where
Self : ::std ::marker ::Sized ,
{
" test::TestAction "
}
fn partial_eq ( & self , action : & dyn Action ) -> bool {
action
. as_any ( )
. downcast_ref ::< Self > ( )
. map_or ( false , | a | self = = a )
}
fn boxed_clone ( & self ) -> std ::boxed ::Box < dyn Action > {
Box ::new ( TestAction )
}
fn as_any ( & self ) -> & dyn ::std ::any ::Any {
self
}
fn build ( _value : serde_json ::Value ) -> anyhow ::Result < Box < dyn Action > >
where
Self : Sized ,
{
Ok ( Box ::new ( TestAction ) )
}
}
#[ test ]
fn test_keybinding_for_action_bounds ( ) {
let keymap = Keymap ::new ( vec! [ KeyBinding ::new (
" cmd-n " ,
TestAction ,
Some ( " ProjectPanel " ) ,
) ] ) ;
let mut registry = ActionRegistry ::default ( ) ;
registry . load_action ::< TestAction > ( ) ;
2024-01-21 23:35:47 +00:00
let keymap = Rc ::new ( RefCell ::new ( keymap ) ) ;
2024-01-09 02:21:54 +00:00
let tree = DispatchTree ::new ( keymap , Rc ::new ( registry ) ) ;
2024-01-09 19:52:03 +00:00
let contexts = vec! [
KeyContext ::parse ( " Workspace " ) . unwrap ( ) ,
KeyContext ::parse ( " ProjectPanel " ) . unwrap ( ) ,
] ;
let keybinding = tree . bindings_for_action ( & TestAction , & contexts ) ;
2024-01-09 02:21:54 +00:00
assert! ( keybinding [ 0 ] . action . partial_eq ( & TestAction ) )
}
}