Compare commits
13 Commits
v0.124.3-p
...
paint-orde
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
92fda552d1 | ||
|
|
0d764afd4c | ||
|
|
678ff835d2 | ||
|
|
f531d953e3 | ||
|
|
6583c69612 | ||
|
|
f930969411 | ||
|
|
266bb62813 | ||
|
|
6e897d9969 | ||
|
|
d90b052162 | ||
|
|
49a53e7654 | ||
|
|
3220986fc9 | ||
|
|
9fcda5a5ac | ||
|
|
5e43290aa1 |
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
@@ -64,6 +64,8 @@ jobs:
|
||||
fi
|
||||
|
||||
- uses: bufbuild/buf-setup-action@v1
|
||||
with:
|
||||
version: v1.29.0
|
||||
- uses: bufbuild/buf-breaking-action@v1
|
||||
with:
|
||||
input: "crates/rpc/proto/"
|
||||
|
||||
2
Cargo.lock
generated
2
Cargo.lock
generated
@@ -11911,7 +11911,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zed"
|
||||
version = "0.124.0"
|
||||
version = "0.125.0"
|
||||
dependencies = [
|
||||
"activity_indicator",
|
||||
"ai",
|
||||
|
||||
@@ -421,14 +421,20 @@ impl CollabTitlebarItem {
|
||||
worktree.root_name()
|
||||
});
|
||||
|
||||
names.next().unwrap_or("")
|
||||
names.next()
|
||||
};
|
||||
let is_project_selected = name.is_some();
|
||||
let name = if let Some(name) = name {
|
||||
util::truncate_and_trailoff(name, MAX_PROJECT_NAME_LENGTH)
|
||||
} else {
|
||||
"Open recent project".to_string()
|
||||
};
|
||||
|
||||
let name = util::truncate_and_trailoff(name, MAX_PROJECT_NAME_LENGTH);
|
||||
let workspace = self.workspace.clone();
|
||||
popover_menu("project_name_trigger")
|
||||
.trigger(
|
||||
Button::new("project_name_trigger", name)
|
||||
.when(!is_project_selected, |b| b.color(Color::Muted))
|
||||
.style(ButtonStyle::Subtle)
|
||||
.label_size(LabelSize::Small)
|
||||
.tooltip(move |cx| Tooltip::text("Recent Projects", cx)),
|
||||
|
||||
@@ -1106,14 +1106,8 @@ impl AppContext {
|
||||
for window in self.windows() {
|
||||
window
|
||||
.update(self, |_, cx| {
|
||||
cx.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.clear_pending_keystrokes();
|
||||
cx.window
|
||||
.next_frame
|
||||
.dispatch_tree
|
||||
.clear_pending_keystrokes();
|
||||
cx.window.rendered_frame.clear_pending_keystrokes();
|
||||
cx.window.next_frame.clear_pending_keystrokes();
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
457
crates/gpui/src/frame.rs
Normal file
457
crates/gpui/src/frame.rs
Normal file
@@ -0,0 +1,457 @@
|
||||
use crate::{
|
||||
Action, ActionRegistry, AnyTooltip, Bounds, ContentMask, CursorStyle, DispatchPhase,
|
||||
ElementContext, EntityId, FocusId, GlobalElementId, KeyBinding, KeyContext, KeyEvent, Keymap,
|
||||
KeymatchResult, Keystroke, KeystrokeMatcher, MouseEvent, Pixels, PlatformInputHandler,
|
||||
Primitive, Scene, SceneIndex, SmallVec, WindowContext,
|
||||
};
|
||||
use collections::FxHashMap;
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
cell::RefCell,
|
||||
iter,
|
||||
ops::Range,
|
||||
rc::Rc,
|
||||
};
|
||||
|
||||
// pub(crate) struct Frame {
|
||||
// pub(crate) window_active: bool,
|
||||
|
||||
// #[cfg(any(test, feature = "test-support"))]
|
||||
// pub(crate) debug_bounds: FxHashMap<String, Bounds<Pixels>>,
|
||||
// }
|
||||
|
||||
pub struct Frame {
|
||||
elements: Vec<PaintedElement>,
|
||||
pub(crate) scene: Scene,
|
||||
focus: Option<FocusId>,
|
||||
pub(crate) window_active: bool,
|
||||
mouse_listeners: Vec<AnyMouseListener>,
|
||||
key_listeners: Vec<KeyListener>,
|
||||
action_listeners: Vec<ActionListener>,
|
||||
element_states: FxHashMap<GlobalElementId, ElementStateBox>,
|
||||
|
||||
element_stack: Vec<PaintedElementId>,
|
||||
context_stack: Vec<KeyContext>,
|
||||
content_mask_stack: Vec<ContentMask<Pixels>>,
|
||||
focusable_node_ids: FxHashMap<FocusId, PaintedElementId>,
|
||||
view_node_ids: FxHashMap<EntityId, PaintedElementId>,
|
||||
keystroke_matchers: FxHashMap<SmallVec<[KeyContext; 4]>, KeystrokeMatcher>,
|
||||
keymap: Rc<RefCell<Keymap>>,
|
||||
action_registry: Rc<ActionRegistry>,
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
pub fn new(keymap: Rc<RefCell<Keymap>>, action_registry: Rc<ActionRegistry>) -> Self {
|
||||
Frame {
|
||||
keymap,
|
||||
action_registry,
|
||||
elements: Vec::new(),
|
||||
scene: Scene::default(),
|
||||
focus: None,
|
||||
window_active: false,
|
||||
mouse_listeners: Vec::new(),
|
||||
key_listeners: Vec::new(),
|
||||
action_listeners: Vec::new(),
|
||||
element_states: FxHashMap::default(),
|
||||
element_stack: Vec::new(),
|
||||
context_stack: Vec::new(),
|
||||
content_mask_stack: Vec::new(),
|
||||
focusable_node_ids: FxHashMap::default(),
|
||||
view_node_ids: FxHashMap::default(),
|
||||
keystroke_matchers: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.elements.clear();
|
||||
self.scene.clear();
|
||||
self.focus = None;
|
||||
self.mouse_listeners.clear();
|
||||
self.key_listeners.clear();
|
||||
self.action_listeners.clear();
|
||||
self.element_states.clear();
|
||||
self.element_stack.clear();
|
||||
self.context_stack.clear();
|
||||
self.content_mask_stack.clear();
|
||||
self.focusable_node_ids.clear();
|
||||
self.view_node_ids.clear();
|
||||
self.keystroke_matchers.clear();
|
||||
}
|
||||
|
||||
pub fn clear_pending_keystrokes(&mut self) {
|
||||
self.keystroke_matchers.clear();
|
||||
}
|
||||
|
||||
/// Preserve keystroke matchers from previous frames to support multi-stroke
|
||||
/// bindings across multiple frames.
|
||||
pub fn preserve_pending_keystrokes(
|
||||
&mut self,
|
||||
prev_frame: &mut Self,
|
||||
focus_id: Option<FocusId>,
|
||||
) {
|
||||
self.context_stack.clear();
|
||||
for element in self.dispatch_path(focus_id) {
|
||||
if let Some(context) = element.key_context.clone() {
|
||||
self.context_stack.push(context);
|
||||
}
|
||||
|
||||
if let Some((context_stack, matcher)) = prev_frame
|
||||
.keystroke_matchers
|
||||
.remove_entry(self.context_stack.as_slice())
|
||||
{
|
||||
self.keystroke_matchers.insert(context_stack, matcher);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set_focus(&mut self, focus_id: Option<FocusId>) {
|
||||
self.focus = focus_id;
|
||||
}
|
||||
|
||||
pub fn set_window_active(&mut self, active: bool) {
|
||||
self.window_active = active;
|
||||
}
|
||||
|
||||
pub fn window_active(&self) -> bool {
|
||||
self.window_active
|
||||
}
|
||||
|
||||
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.elements[node_id.0].parent;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
|
||||
let Some(focus_id) = self.focus else {
|
||||
return SmallVec::new();
|
||||
};
|
||||
|
||||
let mut focus_path = self
|
||||
.dispatch_path(Some(focus_id))
|
||||
.flat_map(|element| element.focus_id)
|
||||
.collect::<SmallVec<[FocusId; 8]>>();
|
||||
focus_path.reverse(); // Reverse the path so it goes from the root to the focused node.
|
||||
focus_path
|
||||
}
|
||||
|
||||
pub fn view_path(&self, view_id: EntityId) -> SmallVec<[EntityId; 8]> {
|
||||
let Some(element_id) = self.view_node_ids.get(&view_id) else {
|
||||
return SmallVec::new();
|
||||
};
|
||||
|
||||
let mut view_path = self
|
||||
.ancestors(Some(*element_id))
|
||||
.flat_map(|element| element.view_id)
|
||||
.collect::<SmallVec<[EntityId; 8]>>();
|
||||
view_path.reverse(); // Reverse the path so it goes from the root to the focused node.
|
||||
view_path
|
||||
}
|
||||
|
||||
pub fn action_dispatch_path(&self, focus_id: Option<FocusId>) -> SmallVec<[ActionListener; 8]> {
|
||||
let mut action_dispatch_path = self
|
||||
.dispatch_path(focus_id)
|
||||
.flat_map(|element| {
|
||||
self.action_listeners[element.action_listeners.clone()]
|
||||
.iter()
|
||||
.cloned()
|
||||
})
|
||||
.collect::<SmallVec<[ActionListener; 8]>>();
|
||||
action_dispatch_path.reverse(); // Reverse the path so it goes from the root to the focused node.
|
||||
action_dispatch_path
|
||||
}
|
||||
|
||||
pub fn key_dispatch_path(&self, focus_id: Option<FocusId>) -> SmallVec<[KeyListener; 8]> {
|
||||
let mut key_dispatch_path: SmallVec<[KeyListener; 8]> = self
|
||||
.dispatch_path(focus_id)
|
||||
.flat_map(|element| {
|
||||
self.key_listeners[element.key_listeners.clone()]
|
||||
.iter()
|
||||
.cloned()
|
||||
})
|
||||
.collect::<SmallVec<[KeyListener; 8]>>();
|
||||
key_dispatch_path.reverse(); // Reverse the path so it goes from the root to the focused node.
|
||||
key_dispatch_path
|
||||
}
|
||||
|
||||
pub fn available_actions(&self, focus_id: Option<FocusId>) -> Vec<Box<dyn Action>> {
|
||||
let mut actions = Vec::<Box<dyn Action>>::new();
|
||||
for ActionListener { action_type, .. } in self.action_dispatch_path(focus_id) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
actions
|
||||
}
|
||||
|
||||
pub fn bindings_for_action(
|
||||
&self,
|
||||
action: &dyn Action,
|
||||
focus_id: Option<FocusId>,
|
||||
) -> Vec<KeyBinding> {
|
||||
let context_stack = self
|
||||
.dispatch_path(focus_id)
|
||||
.flat_map(|element| element.key_context.clone())
|
||||
.collect::<SmallVec<[KeyContext; 8]>>();
|
||||
|
||||
let keymap = self.keymap.borrow();
|
||||
keymap
|
||||
.bindings_for_action(action)
|
||||
.filter(|binding| {
|
||||
for i in 0..context_stack.len() {
|
||||
let context = &context_stack[0..=i];
|
||||
if keymap.binding_enabled(binding, context) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
})
|
||||
.cloned()
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn is_action_available(&self, action: &dyn Action, focus_id: Option<FocusId>) -> bool {
|
||||
for element in self.dispatch_path(focus_id) {
|
||||
if self.action_listeners[element.action_listeners.clone()]
|
||||
.iter()
|
||||
.any(|listener| listener.action_type == action.as_any().type_id())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
pub fn match_keystroke(
|
||||
&mut self,
|
||||
keystroke: &Keystroke,
|
||||
focus_id: Option<FocusId>,
|
||||
) -> KeymatchResult {
|
||||
let mut bindings = SmallVec::<[KeyBinding; 1]>::new();
|
||||
let mut pending = false;
|
||||
|
||||
let mut context_stack: SmallVec<[KeyContext; 4]> = SmallVec::new();
|
||||
|
||||
for element in self.dispatch_path(focus_id) {
|
||||
if let Some(context) = element.key_context.clone() {
|
||||
context_stack.push(context);
|
||||
}
|
||||
}
|
||||
|
||||
while !context_stack.is_empty() {
|
||||
let keystroke_matcher = self
|
||||
.keystroke_matchers
|
||||
.entry(context_stack.clone())
|
||||
.or_insert_with(|| KeystrokeMatcher::new(self.keymap.clone()));
|
||||
|
||||
let result = keystroke_matcher.match_keystroke(keystroke, &context_stack);
|
||||
if result.pending && !pending && !bindings.is_empty() {
|
||||
context_stack.pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
pending = result.pending || pending;
|
||||
for new_binding in result.bindings {
|
||||
match bindings
|
||||
.iter()
|
||||
.position(|el| el.keystrokes.len() < new_binding.keystrokes.len())
|
||||
{
|
||||
Some(idx) => {
|
||||
bindings.insert(idx, new_binding);
|
||||
}
|
||||
None => bindings.push(new_binding),
|
||||
}
|
||||
}
|
||||
context_stack.pop();
|
||||
}
|
||||
|
||||
KeymatchResult { bindings, pending }
|
||||
}
|
||||
|
||||
pub fn has_pending_keystrokes(&self) -> bool {
|
||||
self.keystroke_matchers
|
||||
.iter()
|
||||
.any(|(_, matcher)| matcher.has_pending_keystrokes())
|
||||
}
|
||||
|
||||
fn dispatch_path(&self, focus_id: Option<FocusId>) -> impl Iterator<Item = &PaintedElement> {
|
||||
let mut current_node_id = focus_id
|
||||
.and_then(|focus_id| self.focusable_node_ids.get(&focus_id).copied())
|
||||
.or_else(|| self.elements.is_empty().then(|| PaintedElementId(0)));
|
||||
self.ancestors(current_node_id)
|
||||
}
|
||||
|
||||
fn ancestors(
|
||||
&self,
|
||||
mut current_node_id: Option<PaintedElementId>,
|
||||
) -> impl Iterator<Item = &PaintedElement> {
|
||||
iter::from_fn(move || {
|
||||
let node_id = current_node_id?;
|
||||
current_node_id = self.elements[node_id.0].parent;
|
||||
Some(&self.elements[node_id.0])
|
||||
})
|
||||
}
|
||||
|
||||
pub fn push_element(&mut self) {
|
||||
let parent = self.element_stack.last().copied();
|
||||
let element_id = PaintedElementId(self.elements.len());
|
||||
let scene_index = self.scene.current_index();
|
||||
self.elements.push(PaintedElement {
|
||||
parent,
|
||||
scene_primitives: scene_index.clone()..scene_index,
|
||||
mouse_listeners: self.mouse_listeners.len()..self.mouse_listeners.len(),
|
||||
key_listeners: self.key_listeners.len()..self.key_listeners.len(),
|
||||
action_listeners: self.action_listeners.len()..self.action_listeners.len(),
|
||||
..Default::default()
|
||||
});
|
||||
self.element_stack.push(element_id);
|
||||
}
|
||||
|
||||
pub fn pop_element(&mut self) {
|
||||
let element = &self.elements[self.active_element_id().0];
|
||||
if element.key_context.is_some() {
|
||||
self.context_stack.pop();
|
||||
}
|
||||
self.element_stack.pop();
|
||||
}
|
||||
|
||||
pub fn set_key_context(&mut self, context: KeyContext) {
|
||||
let element_id = self.active_element_id();
|
||||
self.elements[element_id.0].key_context = Some(context.clone());
|
||||
self.context_stack.push(context);
|
||||
}
|
||||
|
||||
pub fn set_focus_id(&mut self, focus_id: FocusId) {
|
||||
let element_id = self.active_element_id();
|
||||
self.elements[element_id.0].focus_id = Some(focus_id);
|
||||
self.focusable_node_ids.insert(focus_id, element_id);
|
||||
}
|
||||
|
||||
pub fn set_view_id(&mut self, view_id: EntityId) {
|
||||
let element_id = self.active_element_id();
|
||||
self.elements[element_id.0].view_id = Some(view_id);
|
||||
self.view_node_ids.insert(view_id, element_id);
|
||||
}
|
||||
|
||||
pub fn paint_primitive<P: Into<Primitive>>(&mut self, build_primitive: impl FnOnce(u32) -> P) {
|
||||
self.scene.paint_primitive(build_primitive);
|
||||
let element_id = self.active_element_id();
|
||||
self.elements[element_id.0].scene_primitives.end = self.scene.current_index();
|
||||
}
|
||||
|
||||
pub fn on_mouse_event<E: MouseEvent>(
|
||||
&mut self,
|
||||
mut listener: impl 'static + FnMut(&E, DispatchPhase, &mut WindowContext),
|
||||
) {
|
||||
self.mouse_listeners.push(Rc::new(move |event, phase, cx| {
|
||||
if let Some(event) = event.downcast_ref::<E>() {
|
||||
listener(event, phase, cx);
|
||||
}
|
||||
}));
|
||||
self.active_element().mouse_listeners.end += 1;
|
||||
}
|
||||
|
||||
pub fn on_key_event<E: KeyEvent>(
|
||||
&mut self,
|
||||
listener: impl Fn(&E, DispatchPhase, &mut WindowContext) + 'static,
|
||||
) {
|
||||
self.key_listeners.push(Rc::new(|event, phase, cx| {
|
||||
if let Some(event) = event.downcast_ref::<E>() {
|
||||
listener(event, phase, cx);
|
||||
}
|
||||
}));
|
||||
self.active_element().key_listeners.end += 1;
|
||||
}
|
||||
|
||||
pub fn on_action(
|
||||
&mut self,
|
||||
action_type: TypeId,
|
||||
listener: Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext) + 'static>,
|
||||
) {
|
||||
self.action_listeners.push(ActionListener {
|
||||
action_type,
|
||||
listener: Rc::new(|event, phase, cx| listener(event, phase, cx)),
|
||||
});
|
||||
|
||||
self.active_element().action_listeners.end += 1;
|
||||
}
|
||||
|
||||
pub fn set_input_handler(&mut self, handler: Option<PlatformInputHandler>) {
|
||||
self.active_element().input_handler = handler;
|
||||
}
|
||||
|
||||
pub fn set_tooltip(&mut self, tooltip: Option<AnyTooltip>) {
|
||||
self.active_element().tooltip = tooltip;
|
||||
}
|
||||
|
||||
pub fn set_cursor_style(&mut self, cursor_style: Option<CursorStyle>) {
|
||||
self.active_element().cursor_style = cursor_style;
|
||||
}
|
||||
|
||||
fn active_element_id(&self) -> PaintedElementId {
|
||||
self.element_stack
|
||||
.last()
|
||||
.copied()
|
||||
.expect("There should be an active element")
|
||||
}
|
||||
|
||||
fn active_element(&mut self) -> &mut PaintedElement {
|
||||
let element_id = self.active_element_id();
|
||||
&mut self.elements[element_id.0]
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct PaintedElement {
|
||||
id: Option<GlobalElementId>,
|
||||
bounds: Bounds<Pixels>,
|
||||
content_mask: ContentMask<Pixels>,
|
||||
opaque: bool,
|
||||
scene_primitives: Range<SceneIndex>,
|
||||
mouse_listeners: Range<usize>,
|
||||
key_listeners: Range<usize>,
|
||||
action_listeners: Range<usize>,
|
||||
input_handler: Option<PlatformInputHandler>,
|
||||
tooltip: Option<AnyTooltip>,
|
||||
cursor_style: Option<CursorStyle>,
|
||||
key_context: Option<KeyContext>,
|
||||
focus_id: Option<FocusId>,
|
||||
view_id: Option<EntityId>,
|
||||
parent: Option<PaintedElementId>,
|
||||
}
|
||||
|
||||
pub(crate) struct ElementStateBox {
|
||||
pub(crate) inner: Box<dyn Any>,
|
||||
pub(crate) parent_view_id: EntityId,
|
||||
#[cfg(debug_assertions)]
|
||||
pub(crate) type_name: &'static str,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
struct PaintedElementId(usize);
|
||||
|
||||
type AnyMouseListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut ElementContext) + 'static>;
|
||||
|
||||
type KeyListener = Rc<dyn Fn(&dyn Any, DispatchPhase, &mut ElementContext)>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct ActionListener {
|
||||
pub(crate) action_type: TypeId,
|
||||
pub(crate) listener: Rc<dyn Fn(&dyn Any, DispatchPhase, &mut WindowContext)>,
|
||||
}
|
||||
@@ -74,6 +74,7 @@ mod color;
|
||||
mod element;
|
||||
mod elements;
|
||||
mod executor;
|
||||
mod frame;
|
||||
mod geometry;
|
||||
mod image_cache;
|
||||
mod input;
|
||||
@@ -122,6 +123,7 @@ pub use ctor::ctor;
|
||||
pub use element::*;
|
||||
pub use elements::*;
|
||||
pub use executor::*;
|
||||
pub use frame::*;
|
||||
pub use geometry::*;
|
||||
pub use gpui_macros::{register_action, test, IntoElement, Render};
|
||||
use image_cache::*;
|
||||
|
||||
@@ -68,26 +68,22 @@ impl<V: 'static> ElementInputHandler<V> {
|
||||
}
|
||||
|
||||
impl<V: ViewInputHandler> InputHandler for ElementInputHandler<V> {
|
||||
fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>> {
|
||||
fn selected_text_range(&self, cx: &mut WindowContext) -> Option<Range<usize>> {
|
||||
self.view
|
||||
.update(cx, |view, cx| view.selected_text_range(cx))
|
||||
}
|
||||
|
||||
fn marked_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>> {
|
||||
fn marked_text_range(&self, cx: &mut WindowContext) -> Option<Range<usize>> {
|
||||
self.view.update(cx, |view, cx| view.marked_text_range(cx))
|
||||
}
|
||||
|
||||
fn text_for_range(
|
||||
&mut self,
|
||||
range_utf16: Range<usize>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Option<String> {
|
||||
fn text_for_range(&self, range_utf16: Range<usize>, cx: &mut WindowContext) -> Option<String> {
|
||||
self.view
|
||||
.update(cx, |view, cx| view.text_for_range(range_utf16, cx))
|
||||
}
|
||||
|
||||
fn replace_text_in_range(
|
||||
&mut self,
|
||||
&self,
|
||||
replacement_range: Option<Range<usize>>,
|
||||
text: &str,
|
||||
cx: &mut WindowContext,
|
||||
@@ -98,7 +94,7 @@ impl<V: ViewInputHandler> InputHandler for ElementInputHandler<V> {
|
||||
}
|
||||
|
||||
fn replace_and_mark_text_in_range(
|
||||
&mut self,
|
||||
&self,
|
||||
range_utf16: Option<Range<usize>>,
|
||||
new_text: &str,
|
||||
new_selected_range: Option<Range<usize>>,
|
||||
@@ -109,12 +105,12 @@ impl<V: ViewInputHandler> InputHandler for ElementInputHandler<V> {
|
||||
});
|
||||
}
|
||||
|
||||
fn unmark_text(&mut self, cx: &mut WindowContext) {
|
||||
fn unmark_text(&self, cx: &mut WindowContext) {
|
||||
self.view.update(cx, |view, cx| view.unmark_text(cx));
|
||||
}
|
||||
|
||||
fn bounds_for_range(
|
||||
&mut self,
|
||||
&self,
|
||||
range_utf16: Range<usize>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Option<Bounds<Pixels>> {
|
||||
|
||||
@@ -108,6 +108,10 @@ impl DispatchTree {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn len(&self) -> usize {
|
||||
self.nodes.len()
|
||||
}
|
||||
|
||||
pub fn clear(&mut self) {
|
||||
self.node_stack.clear();
|
||||
self.context_stack.clear();
|
||||
|
||||
@@ -430,30 +430,26 @@ pub trait InputHandler: 'static {
|
||||
/// Corresponds to [selectedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438242-selectedrange)
|
||||
///
|
||||
/// Return value is in terms of UTF-16 characters, from 0 to the length of the document
|
||||
fn selected_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>>;
|
||||
fn selected_text_range(&self, cx: &mut WindowContext) -> Option<Range<usize>>;
|
||||
|
||||
/// Get the range of the currently marked text, if any
|
||||
/// Corresponds to [markedRange()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438250-markedrange)
|
||||
///
|
||||
/// Return value is in terms of UTF-16 characters, from 0 to the length of the document
|
||||
fn marked_text_range(&mut self, cx: &mut WindowContext) -> Option<Range<usize>>;
|
||||
fn marked_text_range(&self, cx: &mut WindowContext) -> Option<Range<usize>>;
|
||||
|
||||
/// Get the text for the given document range in UTF-16 characters
|
||||
/// Corresponds to [attributedSubstring(forProposedRange: actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438238-attributedsubstring)
|
||||
///
|
||||
/// range_utf16 is in terms of UTF-16 characters
|
||||
fn text_for_range(
|
||||
&mut self,
|
||||
range_utf16: Range<usize>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Option<String>;
|
||||
fn text_for_range(&self, range_utf16: Range<usize>, cx: &mut WindowContext) -> Option<String>;
|
||||
|
||||
/// Replace the text in the given document range with the given text
|
||||
/// Corresponds to [insertText(_:replacementRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438258-inserttext)
|
||||
///
|
||||
/// replacement_range is in terms of UTF-16 characters
|
||||
fn replace_text_in_range(
|
||||
&mut self,
|
||||
&self,
|
||||
replacement_range: Option<Range<usize>>,
|
||||
text: &str,
|
||||
cx: &mut WindowContext,
|
||||
@@ -466,7 +462,7 @@ pub trait InputHandler: 'static {
|
||||
/// range_utf16 is in terms of UTF-16 characters
|
||||
/// new_selected_range is in terms of UTF-16 characters
|
||||
fn replace_and_mark_text_in_range(
|
||||
&mut self,
|
||||
&self,
|
||||
range_utf16: Option<Range<usize>>,
|
||||
new_text: &str,
|
||||
new_selected_range: Option<Range<usize>>,
|
||||
@@ -475,14 +471,14 @@ pub trait InputHandler: 'static {
|
||||
|
||||
/// Remove the IME 'composing' state from the document
|
||||
/// Corresponds to [unmarkText()](https://developer.apple.com/documentation/appkit/nstextinputclient/1438239-unmarktext)
|
||||
fn unmark_text(&mut self, cx: &mut WindowContext);
|
||||
fn unmark_text(&self, cx: &mut WindowContext);
|
||||
|
||||
/// Get the bounds of the given document range in screen coordinates
|
||||
/// Corresponds to [firstRect(forCharacterRange:actualRange:)](https://developer.apple.com/documentation/appkit/nstextinputclient/1438240-firstrect)
|
||||
///
|
||||
/// This is used for positioning the IME candidate window
|
||||
fn bounds_for_range(
|
||||
&mut self,
|
||||
&self,
|
||||
range_utf16: Range<usize>,
|
||||
cx: &mut WindowContext,
|
||||
) -> Option<Bounds<Pixels>>;
|
||||
|
||||
@@ -215,6 +215,15 @@ fn fs_quad(input: QuadVarying) -> @location(0) vec4<f32> {
|
||||
}
|
||||
|
||||
let quad = b_quads[input.quad_id];
|
||||
// Fast path when the quad is not rounded and doesn't have any border.
|
||||
if (quad.corner_radii.top_left == 0.0 && quad.corner_radii.bottom_left == 0.0 &&
|
||||
quad.corner_radii.top_right == 0.0 &&
|
||||
quad.corner_radii.bottom_right == 0.0 && quad.border_widths.top == 0.0 &&
|
||||
quad.border_widths.left == 0.0 && quad.border_widths.right == 0.0 &&
|
||||
quad.border_widths.bottom == 0.0) {
|
||||
return input.background_color;
|
||||
}
|
||||
|
||||
let half_size = quad.bounds.size / 2.0;
|
||||
let center = quad.bounds.origin + half_size;
|
||||
let center_to_point = input.position.xy - center;
|
||||
|
||||
@@ -1,16 +1,12 @@
|
||||
use crate::{
|
||||
point, AtlasTextureId, AtlasTile, Bounds, ContentMask, Corners, Edges, EntityId, Hsla, Pixels,
|
||||
Point, ScaledPixels, StackingOrder,
|
||||
Point, ScaledPixels,
|
||||
};
|
||||
use collections::{BTreeMap, FxHashSet};
|
||||
use std::{fmt::Debug, iter::Peekable, slice};
|
||||
|
||||
#[allow(non_camel_case_types, unused)]
|
||||
pub(crate) type PathVertex_ScaledPixels = PathVertex<ScaledPixels>;
|
||||
|
||||
pub(crate) type LayerId = u32;
|
||||
pub(crate) type DrawOrder = u32;
|
||||
|
||||
#[derive(Default, Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct ViewId {
|
||||
@@ -35,11 +31,20 @@ impl From<ViewId> for EntityId {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Default)]
|
||||
/// An index into all the geometry in a `Scene` at a point in time.
|
||||
pub(crate) struct SceneIndex {
|
||||
pub(crate) shadows: usize,
|
||||
pub(crate) quads: usize,
|
||||
pub(crate) paths: usize,
|
||||
pub(crate) underlines: usize,
|
||||
pub(crate) monochrome_sprites: usize,
|
||||
pub(crate) polychrome_sprites: usize,
|
||||
pub(crate) surfaces: usize,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
pub(crate) struct Scene {
|
||||
last_layer: Option<(StackingOrder, LayerId)>,
|
||||
layers_by_order: BTreeMap<StackingOrder, LayerId>,
|
||||
orders_by_layer: BTreeMap<LayerId, StackingOrder>,
|
||||
pub(crate) shadows: Vec<Shadow>,
|
||||
pub(crate) quads: Vec<Quad>,
|
||||
pub(crate) paths: Vec<Path<ScaledPixels>>,
|
||||
@@ -47,13 +52,11 @@ pub(crate) struct Scene {
|
||||
pub(crate) monochrome_sprites: Vec<MonochromeSprite>,
|
||||
pub(crate) polychrome_sprites: Vec<PolychromeSprite>,
|
||||
pub(crate) surfaces: Vec<Surface>,
|
||||
pub(crate) primitive_count: u32,
|
||||
}
|
||||
|
||||
impl Scene {
|
||||
pub fn clear(&mut self) {
|
||||
self.last_layer = None;
|
||||
self.layers_by_order.clear();
|
||||
self.orders_by_layer.clear();
|
||||
self.shadows.clear();
|
||||
self.quads.clear();
|
||||
self.paths.clear();
|
||||
@@ -61,6 +64,19 @@ impl Scene {
|
||||
self.monochrome_sprites.clear();
|
||||
self.polychrome_sprites.clear();
|
||||
self.surfaces.clear();
|
||||
self.primitive_count = 0;
|
||||
}
|
||||
|
||||
pub fn current_index(&self) -> SceneIndex {
|
||||
SceneIndex {
|
||||
shadows: self.shadows.len(),
|
||||
quads: self.quads.len(),
|
||||
paths: self.paths.len(),
|
||||
underlines: self.underlines.len(),
|
||||
monochrome_sprites: self.monochrome_sprites.len(),
|
||||
polychrome_sprites: self.polychrome_sprites.len(),
|
||||
surfaces: self.surfaces.len(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn paths(&self) -> &[Path<ScaledPixels>] {
|
||||
@@ -93,8 +109,12 @@ impl Scene {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn insert(&mut self, order: &StackingOrder, primitive: impl Into<Primitive>) {
|
||||
let primitive = primitive.into();
|
||||
pub(crate) fn paint_primitive<T: Into<Primitive>>(
|
||||
&mut self,
|
||||
build_primitive: impl FnOnce(u32) -> T,
|
||||
) {
|
||||
let primitive = build_primitive(self.primitive_count).into();
|
||||
|
||||
let clipped_bounds = primitive
|
||||
.bounds()
|
||||
.intersect(&primitive.content_mask().bounds);
|
||||
@@ -104,150 +124,57 @@ impl Scene {
|
||||
return;
|
||||
}
|
||||
|
||||
let layer_id = self.layer_id_for_order(order);
|
||||
match primitive {
|
||||
Primitive::Shadow(mut shadow) => {
|
||||
shadow.layer_id = layer_id;
|
||||
self.shadows.push(shadow);
|
||||
}
|
||||
Primitive::Quad(mut quad) => {
|
||||
quad.layer_id = layer_id;
|
||||
self.quads.push(quad);
|
||||
}
|
||||
Primitive::Path(mut path) => {
|
||||
path.layer_id = layer_id;
|
||||
path.id = PathId(self.paths.len());
|
||||
self.paths.push(path);
|
||||
}
|
||||
Primitive::Underline(mut underline) => {
|
||||
underline.layer_id = layer_id;
|
||||
self.underlines.push(underline);
|
||||
}
|
||||
Primitive::MonochromeSprite(mut sprite) => {
|
||||
sprite.layer_id = layer_id;
|
||||
self.monochrome_sprites.push(sprite);
|
||||
}
|
||||
Primitive::PolychromeSprite(mut sprite) => {
|
||||
sprite.layer_id = layer_id;
|
||||
self.polychrome_sprites.push(sprite);
|
||||
}
|
||||
Primitive::Surface(mut surface) => {
|
||||
surface.layer_id = layer_id;
|
||||
self.surfaces.push(surface);
|
||||
}
|
||||
}
|
||||
|
||||
self.primitive_count += 1;
|
||||
}
|
||||
|
||||
fn layer_id_for_order(&mut self, order: &StackingOrder) -> LayerId {
|
||||
if let Some((last_order, last_layer_id)) = self.last_layer.as_ref() {
|
||||
if order == last_order {
|
||||
return *last_layer_id;
|
||||
}
|
||||
}
|
||||
|
||||
let layer_id = if let Some(layer_id) = self.layers_by_order.get(order) {
|
||||
*layer_id
|
||||
} else {
|
||||
let next_id = self.layers_by_order.len() as LayerId;
|
||||
self.layers_by_order.insert(order.clone(), next_id);
|
||||
self.orders_by_layer.insert(next_id, order.clone());
|
||||
next_id
|
||||
};
|
||||
self.last_layer = Some((order.clone(), layer_id));
|
||||
layer_id
|
||||
}
|
||||
|
||||
pub fn reuse_views(&mut self, views: &FxHashSet<EntityId>, prev_scene: &mut Self) {
|
||||
for shadow in prev_scene.shadows.drain(..) {
|
||||
if views.contains(&shadow.view_id.into()) {
|
||||
let order = &prev_scene.orders_by_layer[&shadow.layer_id];
|
||||
self.insert(order, shadow);
|
||||
}
|
||||
}
|
||||
|
||||
for quad in prev_scene.quads.drain(..) {
|
||||
if views.contains(&quad.view_id.into()) {
|
||||
let order = &prev_scene.orders_by_layer[&quad.layer_id];
|
||||
self.insert(order, quad);
|
||||
}
|
||||
}
|
||||
|
||||
for path in prev_scene.paths.drain(..) {
|
||||
if views.contains(&path.view_id.into()) {
|
||||
let order = &prev_scene.orders_by_layer[&path.layer_id];
|
||||
self.insert(order, path);
|
||||
}
|
||||
}
|
||||
|
||||
for underline in prev_scene.underlines.drain(..) {
|
||||
if views.contains(&underline.view_id.into()) {
|
||||
let order = &prev_scene.orders_by_layer[&underline.layer_id];
|
||||
self.insert(order, underline);
|
||||
}
|
||||
}
|
||||
|
||||
for sprite in prev_scene.monochrome_sprites.drain(..) {
|
||||
if views.contains(&sprite.view_id.into()) {
|
||||
let order = &prev_scene.orders_by_layer[&sprite.layer_id];
|
||||
self.insert(order, sprite);
|
||||
}
|
||||
}
|
||||
|
||||
for sprite in prev_scene.polychrome_sprites.drain(..) {
|
||||
if views.contains(&sprite.view_id.into()) {
|
||||
let order = &prev_scene.orders_by_layer[&sprite.layer_id];
|
||||
self.insert(order, sprite);
|
||||
}
|
||||
}
|
||||
|
||||
for surface in prev_scene.surfaces.drain(..) {
|
||||
if views.contains(&surface.view_id.into()) {
|
||||
let order = &prev_scene.orders_by_layer[&surface.layer_id];
|
||||
self.insert(order, surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn finish(&mut self) {
|
||||
let mut orders = vec![0; self.layers_by_order.len()];
|
||||
for (ix, layer_id) in self.layers_by_order.values().enumerate() {
|
||||
orders[*layer_id as usize] = ix as u32;
|
||||
}
|
||||
|
||||
for shadow in &mut self.shadows {
|
||||
shadow.order = orders[shadow.layer_id as usize];
|
||||
}
|
||||
self.shadows.sort_by_key(|shadow| shadow.order);
|
||||
|
||||
for quad in &mut self.quads {
|
||||
quad.order = orders[quad.layer_id as usize];
|
||||
}
|
||||
self.quads.sort_by_key(|quad| quad.order);
|
||||
|
||||
for path in &mut self.paths {
|
||||
path.order = orders[path.layer_id as usize];
|
||||
}
|
||||
self.paths.sort_by_key(|path| path.order);
|
||||
|
||||
for underline in &mut self.underlines {
|
||||
underline.order = orders[underline.layer_id as usize];
|
||||
}
|
||||
self.underlines.sort_by_key(|underline| underline.order);
|
||||
|
||||
for monochrome_sprite in &mut self.monochrome_sprites {
|
||||
monochrome_sprite.order = orders[monochrome_sprite.layer_id as usize];
|
||||
}
|
||||
self.monochrome_sprites.sort_by_key(|sprite| sprite.order);
|
||||
|
||||
for polychrome_sprite in &mut self.polychrome_sprites {
|
||||
polychrome_sprite.order = orders[polychrome_sprite.layer_id as usize];
|
||||
}
|
||||
self.polychrome_sprites.sort_by_key(|sprite| sprite.order);
|
||||
|
||||
for surface in &mut self.surfaces {
|
||||
surface.order = orders[surface.layer_id as usize];
|
||||
}
|
||||
self.surfaces.sort_by_key(|surface| surface.order);
|
||||
pub fn reuse_subscene(&mut self, prev_scene: &mut Self, start: SceneIndex, end: SceneIndex) {
|
||||
self.shadows
|
||||
.extend(prev_scene.shadows.drain(start.shadows..end.shadows));
|
||||
self.quads
|
||||
.extend(prev_scene.quads.drain(start.quads..end.quads));
|
||||
self.paths
|
||||
.extend(prev_scene.paths.drain(start.paths..end.paths));
|
||||
self.underlines.extend(
|
||||
prev_scene
|
||||
.underlines
|
||||
.drain(start.underlines..end.underlines),
|
||||
);
|
||||
self.monochrome_sprites.extend(
|
||||
prev_scene
|
||||
.monochrome_sprites
|
||||
.drain(start.monochrome_sprites..end.monochrome_sprites),
|
||||
);
|
||||
self.polychrome_sprites.extend(
|
||||
prev_scene
|
||||
.polychrome_sprites
|
||||
.drain(start.polychrome_sprites..end.polychrome_sprites),
|
||||
);
|
||||
self.surfaces
|
||||
.extend(prev_scene.surfaces.drain(start.surfaces..end.surfaces));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -281,25 +208,31 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
let mut orders_and_kinds = [
|
||||
(
|
||||
self.shadows_iter.peek().map(|s| s.order),
|
||||
self.shadows_iter.peek().map(|s| s.draw_order),
|
||||
PrimitiveKind::Shadow,
|
||||
),
|
||||
(self.quads_iter.peek().map(|q| q.order), PrimitiveKind::Quad),
|
||||
(self.paths_iter.peek().map(|q| q.order), PrimitiveKind::Path),
|
||||
(
|
||||
self.underlines_iter.peek().map(|u| u.order),
|
||||
self.quads_iter.peek().map(|q| q.draw_order),
|
||||
PrimitiveKind::Quad,
|
||||
),
|
||||
(
|
||||
self.paths_iter.peek().map(|q| q.draw_order),
|
||||
PrimitiveKind::Path,
|
||||
),
|
||||
(
|
||||
self.underlines_iter.peek().map(|u| u.draw_order),
|
||||
PrimitiveKind::Underline,
|
||||
),
|
||||
(
|
||||
self.monochrome_sprites_iter.peek().map(|s| s.order),
|
||||
self.monochrome_sprites_iter.peek().map(|s| s.draw_order),
|
||||
PrimitiveKind::MonochromeSprite,
|
||||
),
|
||||
(
|
||||
self.polychrome_sprites_iter.peek().map(|s| s.order),
|
||||
self.polychrome_sprites_iter.peek().map(|s| s.draw_order),
|
||||
PrimitiveKind::PolychromeSprite,
|
||||
),
|
||||
(
|
||||
self.surfaces_iter.peek().map(|s| s.order),
|
||||
self.surfaces_iter.peek().map(|s| s.draw_order),
|
||||
PrimitiveKind::Surface,
|
||||
),
|
||||
];
|
||||
@@ -320,7 +253,7 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||
self.shadows_iter.next();
|
||||
while self
|
||||
.shadows_iter
|
||||
.next_if(|shadow| (shadow.order, batch_kind) < max_order_and_kind)
|
||||
.next_if(|shadow| (shadow.draw_order, batch_kind) < max_order_and_kind)
|
||||
.is_some()
|
||||
{
|
||||
shadows_end += 1;
|
||||
@@ -336,7 +269,7 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||
self.quads_iter.next();
|
||||
while self
|
||||
.quads_iter
|
||||
.next_if(|quad| (quad.order, batch_kind) < max_order_and_kind)
|
||||
.next_if(|quad| (quad.draw_order, batch_kind) < max_order_and_kind)
|
||||
.is_some()
|
||||
{
|
||||
quads_end += 1;
|
||||
@@ -350,7 +283,7 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||
self.paths_iter.next();
|
||||
while self
|
||||
.paths_iter
|
||||
.next_if(|path| (path.order, batch_kind) < max_order_and_kind)
|
||||
.next_if(|path| (path.draw_order, batch_kind) < max_order_and_kind)
|
||||
.is_some()
|
||||
{
|
||||
paths_end += 1;
|
||||
@@ -364,7 +297,7 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||
self.underlines_iter.next();
|
||||
while self
|
||||
.underlines_iter
|
||||
.next_if(|underline| (underline.order, batch_kind) < max_order_and_kind)
|
||||
.next_if(|underline| (underline.draw_order, batch_kind) < max_order_and_kind)
|
||||
.is_some()
|
||||
{
|
||||
underlines_end += 1;
|
||||
@@ -382,7 +315,7 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||
while self
|
||||
.monochrome_sprites_iter
|
||||
.next_if(|sprite| {
|
||||
(sprite.order, batch_kind) < max_order_and_kind
|
||||
(sprite.draw_order, batch_kind) < max_order_and_kind
|
||||
&& sprite.tile.texture_id == texture_id
|
||||
})
|
||||
.is_some()
|
||||
@@ -403,7 +336,7 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||
while self
|
||||
.polychrome_sprites_iter
|
||||
.next_if(|sprite| {
|
||||
(sprite.order, batch_kind) < max_order_and_kind
|
||||
(sprite.draw_order, batch_kind) < max_order_and_kind
|
||||
&& sprite.tile.texture_id == texture_id
|
||||
})
|
||||
.is_some()
|
||||
@@ -422,7 +355,7 @@ impl<'a> Iterator for BatchIterator<'a> {
|
||||
self.surfaces_iter.next();
|
||||
while self
|
||||
.surfaces_iter
|
||||
.next_if(|surface| (surface.order, batch_kind) < max_order_and_kind)
|
||||
.next_if(|surface| (surface.draw_order, batch_kind) < max_order_and_kind)
|
||||
.is_some()
|
||||
{
|
||||
surfaces_end += 1;
|
||||
@@ -504,9 +437,7 @@ pub(crate) enum PrimitiveBatch<'a> {
|
||||
#[derive(Default, Debug, Clone, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct Quad {
|
||||
pub view_id: ViewId,
|
||||
pub layer_id: LayerId,
|
||||
pub order: DrawOrder,
|
||||
pub draw_order: u32,
|
||||
pub bounds: Bounds<ScaledPixels>,
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
pub background: Hsla,
|
||||
@@ -517,7 +448,7 @@ pub(crate) struct Quad {
|
||||
|
||||
impl Ord for Quad {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.order.cmp(&other.order)
|
||||
self.draw_order.cmp(&other.draw_order)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -536,9 +467,7 @@ impl From<Quad> for Primitive {
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct Underline {
|
||||
pub view_id: ViewId,
|
||||
pub layer_id: LayerId,
|
||||
pub order: DrawOrder,
|
||||
pub draw_order: u32,
|
||||
pub bounds: Bounds<ScaledPixels>,
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
pub color: Hsla,
|
||||
@@ -548,7 +477,7 @@ pub(crate) struct Underline {
|
||||
|
||||
impl Ord for Underline {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.order.cmp(&other.order)
|
||||
self.draw_order.cmp(&other.draw_order)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -567,9 +496,7 @@ impl From<Underline> for Primitive {
|
||||
#[derive(Debug, Clone, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct Shadow {
|
||||
pub view_id: ViewId,
|
||||
pub layer_id: LayerId,
|
||||
pub order: DrawOrder,
|
||||
pub draw_order: u32,
|
||||
pub bounds: Bounds<ScaledPixels>,
|
||||
pub corner_radii: Corners<ScaledPixels>,
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
@@ -580,7 +507,7 @@ pub(crate) struct Shadow {
|
||||
|
||||
impl Ord for Shadow {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.order.cmp(&other.order)
|
||||
self.draw_order.cmp(&other.draw_order)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,30 +526,13 @@ impl From<Shadow> for Primitive {
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct MonochromeSprite {
|
||||
pub view_id: ViewId,
|
||||
pub layer_id: LayerId,
|
||||
pub order: DrawOrder,
|
||||
pub draw_order: u32,
|
||||
pub bounds: Bounds<ScaledPixels>,
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
pub color: Hsla,
|
||||
pub tile: AtlasTile,
|
||||
}
|
||||
|
||||
impl Ord for MonochromeSprite {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
match self.order.cmp(&other.order) {
|
||||
std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id),
|
||||
order => order,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for MonochromeSprite {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<MonochromeSprite> for Primitive {
|
||||
fn from(sprite: MonochromeSprite) -> Self {
|
||||
Primitive::MonochromeSprite(sprite)
|
||||
@@ -632,9 +542,7 @@ impl From<MonochromeSprite> for Primitive {
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
#[repr(C)]
|
||||
pub(crate) struct PolychromeSprite {
|
||||
pub view_id: ViewId,
|
||||
pub layer_id: LayerId,
|
||||
pub order: DrawOrder,
|
||||
pub draw_order: u32,
|
||||
pub bounds: Bounds<ScaledPixels>,
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
pub corner_radii: Corners<ScaledPixels>,
|
||||
@@ -643,21 +551,6 @@ pub(crate) struct PolychromeSprite {
|
||||
pub pad: u32, // align to 8 bytes
|
||||
}
|
||||
|
||||
impl Ord for PolychromeSprite {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
match self.order.cmp(&other.order) {
|
||||
std::cmp::Ordering::Equal => self.tile.tile_id.cmp(&other.tile.tile_id),
|
||||
order => order,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for PolychromeSprite {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<PolychromeSprite> for Primitive {
|
||||
fn from(sprite: PolychromeSprite) -> Self {
|
||||
Primitive::PolychromeSprite(sprite)
|
||||
@@ -666,27 +559,13 @@ impl From<PolychromeSprite> for Primitive {
|
||||
|
||||
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||
pub(crate) struct Surface {
|
||||
pub view_id: ViewId,
|
||||
pub layer_id: LayerId,
|
||||
pub order: DrawOrder,
|
||||
pub draw_order: u32,
|
||||
pub bounds: Bounds<ScaledPixels>,
|
||||
pub content_mask: ContentMask<ScaledPixels>,
|
||||
#[cfg(target_os = "macos")]
|
||||
pub image_buffer: media::core_video::CVImageBuffer,
|
||||
}
|
||||
|
||||
impl Ord for Surface {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.order.cmp(&other.order)
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for Surface {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Surface> for Primitive {
|
||||
fn from(surface: Surface) -> Self {
|
||||
Primitive::Surface(surface)
|
||||
@@ -699,10 +578,8 @@ pub(crate) struct PathId(pub(crate) usize);
|
||||
/// A line made up of a series of vertices and control points.
|
||||
#[derive(Debug)]
|
||||
pub struct Path<P: Clone + Default + Debug> {
|
||||
pub(crate) draw_order: u32,
|
||||
pub(crate) id: PathId,
|
||||
pub(crate) view_id: ViewId,
|
||||
layer_id: LayerId,
|
||||
order: DrawOrder,
|
||||
pub(crate) bounds: Bounds<P>,
|
||||
pub(crate) content_mask: ContentMask<P>,
|
||||
pub(crate) vertices: Vec<PathVertex<P>>,
|
||||
@@ -716,10 +593,8 @@ impl Path<Pixels> {
|
||||
/// Create a new path with the given starting point.
|
||||
pub fn new(start: Point<Pixels>) -> Self {
|
||||
Self {
|
||||
draw_order: 0,
|
||||
id: PathId(0),
|
||||
view_id: ViewId::default(),
|
||||
layer_id: LayerId::default(),
|
||||
order: DrawOrder::default(),
|
||||
vertices: Vec::new(),
|
||||
start,
|
||||
current: start,
|
||||
@@ -736,10 +611,8 @@ impl Path<Pixels> {
|
||||
/// Scale this path by the given factor.
|
||||
pub fn scale(&self, factor: f32) -> Path<ScaledPixels> {
|
||||
Path {
|
||||
draw_order: self.draw_order,
|
||||
id: self.id,
|
||||
view_id: self.view_id,
|
||||
layer_id: self.layer_id,
|
||||
order: self.order,
|
||||
bounds: self.bounds.scale(factor),
|
||||
content_mask: self.content_mask.scale(factor),
|
||||
vertices: self
|
||||
@@ -825,13 +698,13 @@ impl Eq for Path<ScaledPixels> {}
|
||||
|
||||
impl PartialEq for Path<ScaledPixels> {
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
self.order == other.order
|
||||
self.draw_order == other.draw_order
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for Path<ScaledPixels> {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
self.order.cmp(&other.order)
|
||||
self.draw_order.cmp(&other.draw_order)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
use crate::{
|
||||
seal::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, Bounds,
|
||||
ContentMask, Element, ElementContext, ElementId, Entity, EntityId, Flatten, FocusHandle,
|
||||
FocusableView, IntoElement, LayoutId, Model, Pixels, Point, Render, Size, StackingOrder, Style,
|
||||
TextStyle, ViewContext, VisualContext, WeakModel,
|
||||
FocusableView, FrameIndex, IntoElement, LayoutId, Model, Pixels, Point, Render, Size,
|
||||
StackingOrder, Style, TextStyle, ViewContext, VisualContext, WeakModel,
|
||||
};
|
||||
use anyhow::{Context, Result};
|
||||
use std::{
|
||||
any::{type_name, TypeId},
|
||||
fmt,
|
||||
hash::{Hash, Hasher},
|
||||
ops::Range,
|
||||
};
|
||||
|
||||
/// A view is a piece of state that can be presented on screen by implementing the [Render] trait.
|
||||
@@ -24,15 +25,16 @@ impl<V> Sealed for View<V> {}
|
||||
pub struct AnyViewState {
|
||||
root_style: Style,
|
||||
next_stacking_order_id: u16,
|
||||
cache_key: Option<ViewCacheKey>,
|
||||
cache_state: Option<ViewCacheState>,
|
||||
element: Option<AnyElement>,
|
||||
}
|
||||
|
||||
struct ViewCacheKey {
|
||||
struct ViewCacheState {
|
||||
bounds: Bounds<Pixels>,
|
||||
stacking_order: StackingOrder,
|
||||
content_mask: ContentMask<Pixels>,
|
||||
text_style: TextStyle,
|
||||
subframe_range: Range<FrameIndex>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Entity<V> for View<V> {
|
||||
@@ -297,7 +299,7 @@ impl Element for AnyView {
|
||||
let state = AnyViewState {
|
||||
root_style,
|
||||
next_stacking_order_id: 0,
|
||||
cache_key: None,
|
||||
cache_state: None,
|
||||
element: Some(element),
|
||||
};
|
||||
(layout_id, state)
|
||||
@@ -311,23 +313,25 @@ impl Element for AnyView {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(cache_key) = state.cache_key.as_mut() {
|
||||
if cache_key.bounds == bounds
|
||||
&& cache_key.content_mask == cx.content_mask()
|
||||
&& cache_key.stacking_order == *cx.stacking_order()
|
||||
&& cache_key.text_style == cx.text_style()
|
||||
if let Some(cache_state) = state.cache_state.as_mut() {
|
||||
if cache_state.bounds == bounds
|
||||
&& cache_state.content_mask == cx.content_mask()
|
||||
&& cache_state.stacking_order == *cx.stacking_order()
|
||||
&& cache_state.text_style == cx.text_style()
|
||||
{
|
||||
cx.reuse_view(state.next_stacking_order_id);
|
||||
cx.reuse_view(cache_state.subframe_range.clone());
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let subframe_start = cx.window.next_frame.current_index();
|
||||
if let Some(mut element) = state.element.take() {
|
||||
element.paint(cx);
|
||||
} else {
|
||||
let mut element = (self.request_layout)(self, cx).1;
|
||||
element.draw(bounds.origin, bounds.size.into(), cx);
|
||||
}
|
||||
let subframe_end = cx.window.next_frame.current_index();
|
||||
|
||||
state.next_stacking_order_id = cx
|
||||
.window
|
||||
@@ -336,11 +340,12 @@ impl Element for AnyView {
|
||||
.last()
|
||||
.copied()
|
||||
.unwrap();
|
||||
state.cache_key = Some(ViewCacheKey {
|
||||
state.cache_state = Some(ViewCacheState {
|
||||
bounds,
|
||||
stacking_order: cx.stacking_order().clone(),
|
||||
content_mask: cx.content_mask(),
|
||||
text_style: cx.text_style(),
|
||||
subframe_range: subframe_start..subframe_end,
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
use crate::{
|
||||
px, size, transparent_black, Action, AnyDrag, AnyView, AppContext, Arena, AsyncWindowContext,
|
||||
AvailableSpace, Bounds, Context, Corners, CursorStyle, DispatchActionListener, DispatchNodeId,
|
||||
DispatchTree, DisplayId, Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten,
|
||||
Global, GlobalElementId, Hsla, KeyBinding, KeyContext, KeyDownEvent, KeyMatch, KeymatchResult,
|
||||
frame::ActionListener, px, size, transparent_black, Action, AnyDrag, AnyView, AppContext,
|
||||
Arena, AsyncWindowContext, AvailableSpace, Bounds, Context, Corners, CursorStyle, DisplayId,
|
||||
Edges, Effect, Entity, EntityId, EventEmitter, FileDropEvent, Flatten, Frame, Global,
|
||||
GlobalElementId, Hsla, KeyBinding, KeyContext, KeyDownEvent, KeyMatch, KeymatchResult,
|
||||
Keystroke, KeystrokeEvent, Model, ModelContext, Modifiers, MouseButton, MouseMoveEvent,
|
||||
MouseUpEvent, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformWindow, Point,
|
||||
PromptLevel, Render, ScaledPixels, SharedString, Size, SubscriberSet, Subscription,
|
||||
@@ -126,10 +126,7 @@ impl FocusId {
|
||||
|
||||
/// Obtains whether this handle contains the given handle in the most recently rendered frame.
|
||||
pub(crate) fn contains(&self, other: Self, cx: &WindowContext) -> bool {
|
||||
cx.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.focus_contains(*self, other)
|
||||
cx.window.rendered_frame.focus_contains(*self, other)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -314,13 +311,6 @@ impl PendingInput {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct ElementStateBox {
|
||||
pub(crate) inner: Box<dyn Any>,
|
||||
pub(crate) parent_view_id: EntityId,
|
||||
#[cfg(debug_assertions)]
|
||||
pub(crate) type_name: &'static str,
|
||||
}
|
||||
|
||||
impl Window {
|
||||
pub(crate) fn new(
|
||||
handle: AnyWindowHandle,
|
||||
@@ -449,8 +439,8 @@ impl Window {
|
||||
layout_engine: Some(TaffyLayoutEngine::new()),
|
||||
root_view: None,
|
||||
element_id_stack: GlobalElementId::default(),
|
||||
rendered_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
|
||||
next_frame: Frame::new(DispatchTree::new(cx.keymap.clone(), cx.actions.clone())),
|
||||
rendered_frame: Frame::new(cx.keymap.clone(), cx.actions.clone()),
|
||||
next_frame: Frame::new(cx.keymap.clone(), cx.actions.clone()),
|
||||
next_frame_callbacks,
|
||||
dirty_views: FxHashSet::default(),
|
||||
focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
|
||||
@@ -561,10 +551,7 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
self.window.focus = Some(handle.id);
|
||||
self.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.clear_pending_keystrokes();
|
||||
self.window.rendered_frame.clear_pending_keystrokes();
|
||||
self.refresh();
|
||||
}
|
||||
|
||||
@@ -591,20 +578,10 @@ impl<'a> WindowContext<'a> {
|
||||
|
||||
/// Dispatch the given action on the currently focused element.
|
||||
pub fn dispatch_action(&mut self, action: Box<dyn Action>) {
|
||||
let focus_handle = self.focused();
|
||||
|
||||
let focus_id = self.window.focus;
|
||||
self.defer(move |cx| {
|
||||
let node_id = focus_handle
|
||||
.and_then(|handle| {
|
||||
cx.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.focusable_node_id(handle.id)
|
||||
})
|
||||
.unwrap_or_else(|| cx.window.rendered_frame.dispatch_tree.root_node_id());
|
||||
|
||||
cx.propagate_event = true;
|
||||
cx.dispatch_action_on_node(node_id, action);
|
||||
cx.dispatch_action_on(focus_id, action);
|
||||
})
|
||||
}
|
||||
|
||||
@@ -632,14 +609,8 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
pub(crate) fn clear_pending_keystrokes(&mut self) {
|
||||
self.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.clear_pending_keystrokes();
|
||||
self.window
|
||||
.next_frame
|
||||
.dispatch_tree
|
||||
.clear_pending_keystrokes();
|
||||
self.window.rendered_frame.clear_pending_keystrokes();
|
||||
self.window.next_frame.clear_pending_keystrokes();
|
||||
}
|
||||
|
||||
/// Schedules the given function to be run at the end of the current effect cycle, allowing entities
|
||||
@@ -834,19 +805,9 @@ impl<'a> WindowContext<'a> {
|
||||
|
||||
/// Determine whether the given action is available along the dispatch path to the currently focused element.
|
||||
pub fn is_action_available(&self, action: &dyn Action) -> bool {
|
||||
let target = self
|
||||
.focused()
|
||||
.and_then(|focused_handle| {
|
||||
self.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.focusable_node_id(focused_handle.id)
|
||||
})
|
||||
.unwrap_or_else(|| self.window.rendered_frame.dispatch_tree.root_node_id());
|
||||
self.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.is_action_available(action, target)
|
||||
.is_action_available(action, self.window.focus)
|
||||
}
|
||||
|
||||
/// The position of the mouse relative to the window.
|
||||
@@ -863,36 +824,38 @@ impl<'a> WindowContext<'a> {
|
||||
/// on top of the given level. Layers who are extensions of the queried layer
|
||||
/// are not considered to be on top of queried layer.
|
||||
pub fn was_top_layer(&self, point: &Point<Pixels>, layer: &StackingOrder) -> bool {
|
||||
// Precondition: the depth map is ordered from topmost to bottomost.
|
||||
todo!()
|
||||
|
||||
for (opaque_layer, _, bounds) in self.window.rendered_frame.depth_map.iter() {
|
||||
if layer >= opaque_layer {
|
||||
// The queried layer is either above or is the same as the this opaque layer.
|
||||
// Anything after this point is guaranteed to be below the queried layer.
|
||||
return true;
|
||||
}
|
||||
// // Precondition: the depth map is ordered from topmost to bottomost.
|
||||
|
||||
if !bounds.contains(point) {
|
||||
// This opaque layer is above the queried layer but it doesn't contain
|
||||
// the given position, so we can ignore it even if it's above.
|
||||
continue;
|
||||
}
|
||||
// for (opaque_layer, _, bounds) in self.window.rendered_frame.depth_map.iter() {
|
||||
// if layer >= opaque_layer {
|
||||
// // The queried layer is either above or is the same as the this opaque layer.
|
||||
// // Anything after this point is guaranteed to be below the queried layer.
|
||||
// return true;
|
||||
// }
|
||||
|
||||
// At this point, we've established that this opaque layer is on top of the queried layer
|
||||
// and contains the position:
|
||||
// If neither the opaque layer or the queried layer is an extension of the other then
|
||||
// we know they are on different stacking orders, and return false.
|
||||
let is_on_same_layer = opaque_layer
|
||||
.iter()
|
||||
.zip(layer.iter())
|
||||
.all(|(a, b)| a.z_index == b.z_index);
|
||||
// if !bounds.contains(point) {
|
||||
// // This opaque layer is above the queried layer but it doesn't contain
|
||||
// // the given position, so we can ignore it even if it's above.
|
||||
// continue;
|
||||
// }
|
||||
|
||||
if !is_on_same_layer {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// // At this point, we've established that this opaque layer is on top of the queried layer
|
||||
// // and contains the position:
|
||||
// // If neither the opaque layer or the queried layer is an extension of the other then
|
||||
// // we know they are on different stacking orders, and return false.
|
||||
// let is_on_same_layer = opaque_layer
|
||||
// .iter()
|
||||
// .zip(layer.iter())
|
||||
// .all(|(a, b)| a.z_index == b.z_index);
|
||||
|
||||
true
|
||||
// if !is_on_same_layer {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
// true
|
||||
}
|
||||
|
||||
pub(crate) fn was_top_layer_under_active_drag(
|
||||
@@ -900,52 +863,53 @@ impl<'a> WindowContext<'a> {
|
||||
point: &Point<Pixels>,
|
||||
layer: &StackingOrder,
|
||||
) -> bool {
|
||||
todo!()
|
||||
// Precondition: the depth map is ordered from topmost to bottomost.
|
||||
|
||||
for (opaque_layer, _, bounds) in self.window.rendered_frame.depth_map.iter() {
|
||||
if layer >= opaque_layer {
|
||||
// The queried layer is either above or is the same as the this opaque layer.
|
||||
// Anything after this point is guaranteed to be below the queried layer.
|
||||
return true;
|
||||
}
|
||||
// for (opaque_layer, _, bounds) in self.window.rendered_frame.depth_map.iter() {
|
||||
// if layer >= opaque_layer {
|
||||
// // The queried layer is either above or is the same as the this opaque layer.
|
||||
// // Anything after this point is guaranteed to be below the queried layer.
|
||||
// return true;
|
||||
// }
|
||||
|
||||
if !bounds.contains(point) {
|
||||
// This opaque layer is above the queried layer but it doesn't contain
|
||||
// the given position, so we can ignore it even if it's above.
|
||||
continue;
|
||||
}
|
||||
// if !bounds.contains(point) {
|
||||
// // This opaque layer is above the queried layer but it doesn't contain
|
||||
// // the given position, so we can ignore it even if it's above.
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// All normal content is rendered with a base z-index of 0, we know that if the root of this opaque layer
|
||||
// equals `ACTIVE_DRAG_Z_INDEX` then it must be the drag layer and we can ignore it as we are
|
||||
// looking to see if the queried layer was the topmost underneath the drag layer.
|
||||
if opaque_layer
|
||||
.first()
|
||||
.map(|c| c.z_index == ACTIVE_DRAG_Z_INDEX)
|
||||
.unwrap_or(false)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
// // All normal content is rendered with a base z-index of 0, we know that if the root of this opaque layer
|
||||
// // equals `ACTIVE_DRAG_Z_INDEX` then it must be the drag layer and we can ignore it as we are
|
||||
// // looking to see if the queried layer was the topmost underneath the drag layer.
|
||||
// if opaque_layer
|
||||
// .first()
|
||||
// .map(|c| c.z_index == ACTIVE_DRAG_Z_INDEX)
|
||||
// .unwrap_or(false)
|
||||
// {
|
||||
// continue;
|
||||
// }
|
||||
|
||||
// At this point, we've established that this opaque layer is on top of the queried layer
|
||||
// and contains the position:
|
||||
// If neither the opaque layer or the queried layer is an extension of the other then
|
||||
// we know they are on different stacking orders, and return false.
|
||||
let is_on_same_layer = opaque_layer
|
||||
.iter()
|
||||
.zip(layer.iter())
|
||||
.all(|(a, b)| a.z_index == b.z_index);
|
||||
// // At this point, we've established that this opaque layer is on top of the queried layer
|
||||
// // and contains the position:
|
||||
// // If neither the opaque layer or the queried layer is an extension of the other then
|
||||
// // we know they are on different stacking orders, and return false.
|
||||
// let is_on_same_layer = opaque_layer
|
||||
// .iter()
|
||||
// .zip(layer.iter())
|
||||
// .all(|(a, b)| a.z_index == b.z_index);
|
||||
|
||||
if !is_on_same_layer {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// if !is_on_same_layer {
|
||||
// return false;
|
||||
// }
|
||||
// }
|
||||
|
||||
true
|
||||
// true
|
||||
}
|
||||
|
||||
/// Called during painting to get the current stacking order.
|
||||
pub fn stacking_order(&self) -> &StackingOrder {
|
||||
&self.window.next_frame.z_index_stack
|
||||
todo!()
|
||||
}
|
||||
|
||||
/// Produces a new frame and assigns it to `rendered_frame`. To actually show
|
||||
@@ -954,12 +918,6 @@ impl<'a> WindowContext<'a> {
|
||||
self.window.dirty.set(false);
|
||||
self.window.drawing = true;
|
||||
|
||||
if let Some(requested_handler) = self.window.rendered_frame.requested_input_handler.as_mut()
|
||||
{
|
||||
let input_handler = self.window.platform_window.take_input_handler();
|
||||
requested_handler.handler = input_handler;
|
||||
}
|
||||
|
||||
let root_view = self.window.root_view.take().unwrap();
|
||||
self.with_element_context(|cx| {
|
||||
cx.with_z_index(0, |cx| {
|
||||
@@ -967,7 +925,7 @@ impl<'a> WindowContext<'a> {
|
||||
// We need to use cx.cx here so we can utilize borrow splitting
|
||||
for (action_type, action_listeners) in &cx.cx.app.global_action_listeners {
|
||||
for action_listener in action_listeners.iter().cloned() {
|
||||
cx.cx.window.next_frame.dispatch_tree.on_action(
|
||||
cx.cx.window.next_frame.on_action(
|
||||
*action_type,
|
||||
Rc::new(
|
||||
move |action: &dyn Any, phase, cx: &mut WindowContext<'_>| {
|
||||
@@ -1010,15 +968,14 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
self.window.dirty_views.clear();
|
||||
|
||||
self.window.next_frame.preserve_pending_keystrokes(
|
||||
&mut self.window.rendered_frame.dispatch_tree,
|
||||
self.window.focus,
|
||||
);
|
||||
self.window.next_frame.set_focus(self.window.focus);
|
||||
self.window
|
||||
.next_frame
|
||||
.dispatch_tree
|
||||
.preserve_pending_keystrokes(
|
||||
&mut self.window.rendered_frame.dispatch_tree,
|
||||
self.window.focus,
|
||||
);
|
||||
self.window.next_frame.focus = self.window.focus;
|
||||
self.window.next_frame.window_active = self.window.active.get();
|
||||
.set_window_active(self.window.active.get());
|
||||
self.window.root_view = Some(root_view);
|
||||
|
||||
// Set the cursor only if we're the active window.
|
||||
@@ -1042,9 +999,10 @@ impl<'a> WindowContext<'a> {
|
||||
self.window.layout_engine.as_mut().unwrap().clear();
|
||||
self.text_system()
|
||||
.finish_frame(&self.window.next_frame.reused_views);
|
||||
self.window
|
||||
.next_frame
|
||||
.finish(&mut self.window.rendered_frame);
|
||||
// todo!()
|
||||
// self.window
|
||||
// .next_frame
|
||||
// .finish(&mut self.window.rendered_frame);
|
||||
ELEMENT_ARENA.with_borrow_mut(|element_arena| {
|
||||
let percentage = (element_arena.len() as f32 / element_arena.capacity() as f32) * 100.;
|
||||
if percentage >= 80. {
|
||||
@@ -1226,56 +1184,54 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
|
||||
fn dispatch_mouse_event(&mut self, event: &dyn Any) {
|
||||
if let Some(mut handlers) = self
|
||||
.window
|
||||
.rendered_frame
|
||||
.mouse_listeners
|
||||
.remove(&event.type_id())
|
||||
{
|
||||
// Because handlers may add other handlers, we sort every time.
|
||||
handlers.sort_by(|(a, _, _), (b, _, _)| a.cmp(b));
|
||||
todo!()
|
||||
// if let Some(mut handlers) = self
|
||||
// .window
|
||||
// .rendered_frame
|
||||
// .mouse_listeners
|
||||
// .remove(&event.type_id())
|
||||
// {
|
||||
// // Capture phase, events bubble from back to front. Handlers for this phase are used for
|
||||
// // special purposes, such as detecting events outside of a given Bounds.
|
||||
// for (_, _, handler) in &mut handlers {
|
||||
// self.with_element_context(|cx| {
|
||||
// handler(event, DispatchPhase::Capture, cx);
|
||||
// });
|
||||
// if !self.app.propagate_event {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
|
||||
// Capture phase, events bubble from back to front. Handlers for this phase are used for
|
||||
// special purposes, such as detecting events outside of a given Bounds.
|
||||
for (_, _, handler) in &mut handlers {
|
||||
self.with_element_context(|cx| {
|
||||
handler(event, DispatchPhase::Capture, cx);
|
||||
});
|
||||
if !self.app.propagate_event {
|
||||
break;
|
||||
}
|
||||
}
|
||||
// // Bubble phase, where most normal handlers do their work.
|
||||
// if self.app.propagate_event {
|
||||
// for (_, _, handler) in handlers.iter_mut().rev() {
|
||||
// self.with_element_context(|cx| {
|
||||
// handler(event, DispatchPhase::Bubble, cx);
|
||||
// });
|
||||
// if !self.app.propagate_event {
|
||||
// break;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// Bubble phase, where most normal handlers do their work.
|
||||
if self.app.propagate_event {
|
||||
for (_, _, handler) in handlers.iter_mut().rev() {
|
||||
self.with_element_context(|cx| {
|
||||
handler(event, DispatchPhase::Bubble, cx);
|
||||
});
|
||||
if !self.app.propagate_event {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// self.window
|
||||
// .rendered_frame
|
||||
// .mouse_listeners
|
||||
// .insert(event.type_id(), handlers);
|
||||
// }
|
||||
|
||||
self.window
|
||||
.rendered_frame
|
||||
.mouse_listeners
|
||||
.insert(event.type_id(), handlers);
|
||||
}
|
||||
|
||||
if self.app.propagate_event && self.has_active_drag() {
|
||||
if event.is::<MouseMoveEvent>() {
|
||||
// If this was a mouse move event, redraw the window so that the
|
||||
// active drag can follow the mouse cursor.
|
||||
self.refresh();
|
||||
} else if event.is::<MouseUpEvent>() {
|
||||
// If this was a mouse up event, cancel the active drag and redraw
|
||||
// the window.
|
||||
self.active_drag = None;
|
||||
self.refresh();
|
||||
}
|
||||
}
|
||||
// if self.app.propagate_event && self.has_active_drag() {
|
||||
// if event.is::<MouseMoveEvent>() {
|
||||
// // If this was a mouse move event, redraw the window so that the
|
||||
// // active drag can follow the mouse cursor.
|
||||
// self.refresh();
|
||||
// } else if event.is::<MouseUpEvent>() {
|
||||
// // If this was a mouse up event, cancel the active drag and redraw
|
||||
// // the window.
|
||||
// self.active_drag = None;
|
||||
// self.refresh();
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
fn dispatch_key_event(&mut self, event: &dyn Any) {
|
||||
@@ -1283,29 +1239,14 @@ impl<'a> WindowContext<'a> {
|
||||
self.draw();
|
||||
}
|
||||
|
||||
let node_id = self
|
||||
.window
|
||||
.focus
|
||||
.and_then(|focus_id| {
|
||||
self.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.focusable_node_id(focus_id)
|
||||
})
|
||||
.unwrap_or_else(|| self.window.rendered_frame.dispatch_tree.root_node_id());
|
||||
|
||||
let dispatch_path = self
|
||||
.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.dispatch_path(node_id);
|
||||
let focus_id = self.window.focus;
|
||||
let dispatch_path = self.window.rendered_frame.key_dispatch_path(focus_id);
|
||||
|
||||
if let Some(key_down_event) = event.downcast_ref::<KeyDownEvent>() {
|
||||
let KeymatchResult { bindings, pending } = self
|
||||
.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.dispatch_key(&key_down_event.keystroke, &dispatch_path);
|
||||
.match_keystroke(&key_down_event.keystroke, focus_id);
|
||||
|
||||
if pending {
|
||||
let mut currently_pending = self.window.pending_input.take().unwrap_or_default();
|
||||
@@ -1335,8 +1276,11 @@ impl<'a> WindowContext<'a> {
|
||||
self.window.pending_input = Some(currently_pending);
|
||||
|
||||
self.propagate_event = false;
|
||||
|
||||
return;
|
||||
} else if let Some(currently_pending) = self.window.pending_input.take() {
|
||||
}
|
||||
|
||||
if let Some(currently_pending) = self.window.pending_input.take() {
|
||||
if bindings
|
||||
.iter()
|
||||
.all(|binding| !currently_pending.used_by_binding(binding))
|
||||
@@ -1351,7 +1295,7 @@ impl<'a> WindowContext<'a> {
|
||||
|
||||
self.propagate_event = true;
|
||||
for binding in bindings {
|
||||
self.dispatch_action_on_node(node_id, binding.action.boxed_clone());
|
||||
self.dispatch_action_on(focus_id, binding.action.boxed_clone());
|
||||
if !self.propagate_event {
|
||||
self.dispatch_keystroke_observers(event, Some(binding.action));
|
||||
return;
|
||||
@@ -1359,7 +1303,7 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
self.dispatch_key_down_up_event(event, &dispatch_path);
|
||||
self.dispatch_key_down_up_event(event, focus_id);
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
@@ -1367,61 +1311,37 @@ impl<'a> WindowContext<'a> {
|
||||
self.dispatch_keystroke_observers(event, None);
|
||||
}
|
||||
|
||||
fn dispatch_key_down_up_event(
|
||||
&mut self,
|
||||
event: &dyn Any,
|
||||
dispatch_path: &SmallVec<[DispatchNodeId; 32]>,
|
||||
) {
|
||||
fn dispatch_key_down_up_event(&mut self, event: &dyn Any, focus_id: Option<FocusId>) {
|
||||
// Capture phase
|
||||
for node_id in dispatch_path {
|
||||
let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
|
||||
|
||||
for key_listener in node.key_listeners.clone() {
|
||||
self.with_element_context(|cx| {
|
||||
key_listener(event, DispatchPhase::Capture, cx);
|
||||
});
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
let key_dispatch_path = self.window.rendered_frame.key_dispatch_path(focus_id);
|
||||
for key_listener in &key_dispatch_path {
|
||||
self.with_element_context(|cx| {
|
||||
key_listener(event, DispatchPhase::Capture, cx);
|
||||
});
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Bubble phase
|
||||
for node_id in dispatch_path.iter().rev() {
|
||||
// Handle low level key events
|
||||
let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
|
||||
for key_listener in node.key_listeners.clone() {
|
||||
self.with_element_context(|cx| {
|
||||
key_listener(event, DispatchPhase::Bubble, cx);
|
||||
});
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
for key_listener in key_dispatch_path.iter().rev() {
|
||||
self.with_element_context(|cx| {
|
||||
key_listener(event, DispatchPhase::Bubble, cx);
|
||||
});
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Determine whether a potential multi-stroke key binding is in progress on this window.
|
||||
pub fn has_pending_keystrokes(&self) -> bool {
|
||||
self.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.has_pending_keystrokes()
|
||||
self.window.rendered_frame.has_pending_keystrokes()
|
||||
}
|
||||
|
||||
fn replay_pending_input(&mut self, currently_pending: PendingInput) {
|
||||
let node_id = self
|
||||
.window
|
||||
.focus
|
||||
.and_then(|focus_id| {
|
||||
self.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.focusable_node_id(focus_id)
|
||||
})
|
||||
.unwrap_or_else(|| self.window.rendered_frame.dispatch_tree.root_node_id());
|
||||
|
||||
if self.window.focus != currently_pending.focus {
|
||||
let focus_id = self.window.focus;
|
||||
if focus_id != currently_pending.focus {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -1429,25 +1349,19 @@ impl<'a> WindowContext<'a> {
|
||||
|
||||
self.propagate_event = true;
|
||||
for binding in currently_pending.bindings {
|
||||
self.dispatch_action_on_node(node_id, binding.action.boxed_clone());
|
||||
self.dispatch_action_on(focus_id, binding.action.boxed_clone());
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let dispatch_path = self
|
||||
.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.dispatch_path(node_id);
|
||||
|
||||
for keystroke in currently_pending.keystrokes {
|
||||
let event = KeyDownEvent {
|
||||
keystroke,
|
||||
is_held: false,
|
||||
};
|
||||
|
||||
self.dispatch_key_down_up_event(&event, &dispatch_path);
|
||||
self.dispatch_key_down_up_event(&event, focus_id);
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
@@ -1461,52 +1375,43 @@ impl<'a> WindowContext<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
fn dispatch_action_on_node(&mut self, node_id: DispatchNodeId, action: Box<dyn Action>) {
|
||||
let dispatch_path = self
|
||||
.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.dispatch_path(node_id);
|
||||
fn dispatch_action_on(&mut self, focus_id: Option<FocusId>, action: Box<dyn Action>) {
|
||||
let dispatch_path = self.window.rendered_frame.action_dispatch_path(focus_id);
|
||||
|
||||
// Capture phase
|
||||
for node_id in &dispatch_path {
|
||||
let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
|
||||
for DispatchActionListener {
|
||||
action_type,
|
||||
listener,
|
||||
} in node.action_listeners.clone()
|
||||
{
|
||||
let any_action = action.as_any();
|
||||
if action_type == any_action.type_id() {
|
||||
self.with_element_context(|cx| {
|
||||
listener(any_action, DispatchPhase::Capture, cx);
|
||||
});
|
||||
for ActionListener {
|
||||
action_type,
|
||||
listener,
|
||||
} in &dispatch_path
|
||||
{
|
||||
let any_action = action.as_any();
|
||||
if *action_type == any_action.type_id() {
|
||||
self.with_element_context(|cx| {
|
||||
listener(any_action, DispatchPhase::Capture, cx);
|
||||
});
|
||||
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Bubble phase
|
||||
for node_id in dispatch_path.iter().rev() {
|
||||
let node = self.window.rendered_frame.dispatch_tree.node(*node_id);
|
||||
for DispatchActionListener {
|
||||
action_type,
|
||||
listener,
|
||||
} in node.action_listeners.clone()
|
||||
{
|
||||
let any_action = action.as_any();
|
||||
if action_type == any_action.type_id() {
|
||||
self.propagate_event = false; // Actions stop propagation by default during the bubble phase
|
||||
for ActionListener {
|
||||
action_type,
|
||||
listener,
|
||||
} in dispatch_path.iter().rev()
|
||||
{
|
||||
let any_action = action.as_any();
|
||||
if *action_type == any_action.type_id() {
|
||||
self.propagate_event = false; // Actions stop propagation by default during the bubble phase
|
||||
|
||||
self.with_element_context(|cx| {
|
||||
listener(any_action, DispatchPhase::Bubble, cx);
|
||||
});
|
||||
self.with_element_context(|cx| {
|
||||
listener(any_action, DispatchPhase::Bubble, cx);
|
||||
});
|
||||
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
if !self.propagate_event {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1567,32 +1472,16 @@ impl<'a> WindowContext<'a> {
|
||||
|
||||
/// Returns all available actions for the focused element.
|
||||
pub fn available_actions(&self) -> Vec<Box<dyn Action>> {
|
||||
let node_id = self
|
||||
.window
|
||||
.focus
|
||||
.and_then(|focus_id| {
|
||||
self.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.focusable_node_id(focus_id)
|
||||
})
|
||||
.unwrap_or_else(|| self.window.rendered_frame.dispatch_tree.root_node_id());
|
||||
|
||||
self.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.available_actions(node_id)
|
||||
.available_actions(self.window.focus)
|
||||
}
|
||||
|
||||
/// Returns key bindings that invoke the given action on the currently focused element.
|
||||
pub fn bindings_for_action(&self, action: &dyn Action) -> Vec<KeyBinding> {
|
||||
self.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.bindings_for_action(
|
||||
action,
|
||||
&self.window.rendered_frame.dispatch_tree.context_stack,
|
||||
)
|
||||
.bindings_for_action(action, self.window.focus)
|
||||
}
|
||||
|
||||
/// Returns any bindings that would invoke the given action on the given focus handle if it were focused.
|
||||
@@ -1601,17 +1490,9 @@ impl<'a> WindowContext<'a> {
|
||||
action: &dyn Action,
|
||||
focus_handle: &FocusHandle,
|
||||
) -> Vec<KeyBinding> {
|
||||
let dispatch_tree = &self.window.rendered_frame.dispatch_tree;
|
||||
|
||||
let Some(node_id) = dispatch_tree.focusable_node_id(focus_handle.id) else {
|
||||
return vec![];
|
||||
};
|
||||
let context_stack: Vec<_> = dispatch_tree
|
||||
.dispatch_path(node_id)
|
||||
.into_iter()
|
||||
.filter_map(|node_id| dispatch_tree.node(node_id).context.clone())
|
||||
.collect();
|
||||
dispatch_tree.bindings_for_action(action, &context_stack)
|
||||
self.window
|
||||
.rendered_frame
|
||||
.bindings_for_action(action, Some(focus_handle.id))
|
||||
}
|
||||
|
||||
/// Returns a generic event listener that invokes the given listener with the view and context associated with the given view handle.
|
||||
@@ -1647,15 +1528,6 @@ impl<'a> WindowContext<'a> {
|
||||
.on_should_close(Box::new(move || this.update(|cx| f(cx)).unwrap_or(true)))
|
||||
}
|
||||
|
||||
pub(crate) fn parent_view_id(&self) -> EntityId {
|
||||
*self
|
||||
.window
|
||||
.next_frame
|
||||
.view_stack
|
||||
.last()
|
||||
.expect("a view should always be on the stack while drawing")
|
||||
}
|
||||
|
||||
/// Register an action listener on the window for the next frame. The type of action
|
||||
/// is determined by the first parameter of the given listener. When the next frame is rendered
|
||||
/// the listener will be cleared.
|
||||
@@ -1669,7 +1541,6 @@ impl<'a> WindowContext<'a> {
|
||||
) {
|
||||
self.window
|
||||
.next_frame
|
||||
.dispatch_tree
|
||||
.on_action(action_type, Rc::new(listener));
|
||||
}
|
||||
}
|
||||
@@ -2081,7 +1952,6 @@ impl<'a, V: 'static> ViewContext<'a, V> {
|
||||
for view_id in self
|
||||
.window
|
||||
.rendered_frame
|
||||
.dispatch_tree
|
||||
.view_path(self.view.entity_id())
|
||||
.into_iter()
|
||||
.rev()
|
||||
|
||||
@@ -16,12 +16,13 @@ use std::{
|
||||
any::{Any, TypeId},
|
||||
borrow::{Borrow, BorrowMut, Cow},
|
||||
mem,
|
||||
ops::Range,
|
||||
rc::Rc,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
use anyhow::Result;
|
||||
use collections::{FxHashMap, FxHashSet};
|
||||
use collections::FxHashMap;
|
||||
use derive_more::{Deref, DerefMut};
|
||||
#[cfg(target_os = "macos")]
|
||||
use media::core_video::CVImageBuffer;
|
||||
@@ -34,132 +35,135 @@ use crate::{
|
||||
EntityId, FocusHandle, FocusId, FontId, GlobalElementId, GlyphId, Hsla, ImageData,
|
||||
InputHandler, IsZero, KeyContext, KeyEvent, LayoutId, MonochromeSprite, MouseEvent, PaintQuad,
|
||||
Path, Pixels, PlatformInputHandler, Point, PolychromeSprite, Quad, RenderGlyphParams,
|
||||
RenderImageParams, RenderSvgParams, Scene, Shadow, SharedString, Size, StackingContext,
|
||||
StackingOrder, StrikethroughStyle, Style, TextStyleRefinement, Underline, UnderlineStyle,
|
||||
Window, WindowContext, SUBPIXEL_VARIANTS,
|
||||
RenderImageParams, RenderSvgParams, Scene, SceneIndex, Shadow, SharedString, Size,
|
||||
StackingContext, StackingOrder, StrikethroughStyle, Style, TextStyleRefinement, Underline,
|
||||
UnderlineStyle, Window, WindowContext, SUBPIXEL_VARIANTS,
|
||||
};
|
||||
|
||||
type AnyMouseListener = Box<dyn FnMut(&dyn Any, DispatchPhase, &mut ElementContext) + 'static>;
|
||||
|
||||
pub(crate) struct RequestedInputHandler {
|
||||
pub(crate) view_id: EntityId,
|
||||
pub(crate) handler: Option<PlatformInputHandler>,
|
||||
}
|
||||
|
||||
pub(crate) struct TooltipRequest {
|
||||
pub(crate) view_id: EntityId,
|
||||
pub(crate) tooltip: AnyTooltip,
|
||||
}
|
||||
|
||||
pub(crate) struct Frame {
|
||||
pub(crate) focus: Option<FocusId>,
|
||||
pub(crate) window_active: bool,
|
||||
pub(crate) element_states: FxHashMap<GlobalElementId, ElementStateBox>,
|
||||
pub(crate) mouse_listeners: FxHashMap<TypeId, Vec<(StackingOrder, EntityId, AnyMouseListener)>>,
|
||||
pub(crate) dispatch_tree: DispatchTree,
|
||||
pub(crate) scene: Scene,
|
||||
pub(crate) depth_map: Vec<(StackingOrder, EntityId, Bounds<Pixels>)>,
|
||||
pub(crate) z_index_stack: StackingOrder,
|
||||
pub(crate) next_stacking_order_ids: Vec<u16>,
|
||||
pub(crate) next_root_z_index: u16,
|
||||
pub(crate) content_mask_stack: Vec<ContentMask<Pixels>>,
|
||||
pub(crate) element_offset_stack: Vec<Point<Pixels>>,
|
||||
pub(crate) requested_input_handler: Option<RequestedInputHandler>,
|
||||
pub(crate) tooltip_request: Option<TooltipRequest>,
|
||||
pub(crate) cursor_styles: FxHashMap<EntityId, CursorStyle>,
|
||||
pub(crate) requested_cursor_style: Option<CursorStyle>,
|
||||
pub(crate) view_stack: Vec<EntityId>,
|
||||
pub(crate) reused_views: FxHashSet<EntityId>,
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
pub(crate) debug_bounds: FxHashMap<String, Bounds<Pixels>>,
|
||||
/// Identifies a moment in time during construction of a frame. Used for reusing cached subsets of a previous frame.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct FrameIndex {
|
||||
mouse_listeners: usize,
|
||||
dispatch_nodes: usize,
|
||||
scene_index: SceneIndex,
|
||||
}
|
||||
|
||||
impl Frame {
|
||||
pub(crate) fn new(dispatch_tree: DispatchTree) -> Self {
|
||||
Frame {
|
||||
focus: None,
|
||||
window_active: false,
|
||||
element_states: FxHashMap::default(),
|
||||
mouse_listeners: FxHashMap::default(),
|
||||
dispatch_tree,
|
||||
scene: Scene::default(),
|
||||
depth_map: Vec::new(),
|
||||
z_index_stack: StackingOrder::default(),
|
||||
next_stacking_order_ids: vec![0],
|
||||
next_root_z_index: 0,
|
||||
content_mask_stack: Vec::new(),
|
||||
element_offset_stack: Vec::new(),
|
||||
requested_input_handler: None,
|
||||
tooltip_request: None,
|
||||
cursor_styles: FxHashMap::default(),
|
||||
requested_cursor_style: None,
|
||||
view_stack: Vec::new(),
|
||||
reused_views: FxHashSet::default(),
|
||||
// pub(crate) struct Frame {
|
||||
// pub(crate) focus: Option<FocusId>,
|
||||
// pub(crate) window_active: bool,
|
||||
// pub(crate) element_states: FxHashMap<GlobalElementId, ElementStateBox>,
|
||||
// pub(crate) mouse_listeners: FxHashMap<TypeId, Vec<(StackingOrder, EntityId, AnyMouseListener)>>,
|
||||
// pub(crate) input_handlers: Vec<PlatformInputHandler>,
|
||||
// pub(crate) dispatch_tree: DispatchTree,
|
||||
// pub(crate) scene: Scene,
|
||||
// pub(crate) depth_map: Vec<(StackingOrder, EntityId, Bounds<Pixels>)>,
|
||||
// pub(crate) z_index_stack: StackingOrder,
|
||||
// pub(crate) next_stacking_order_ids: Vec<u16>,
|
||||
// pub(crate) next_root_z_index: u16,
|
||||
// pub(crate) content_mask_stack: Vec<ContentMask<Pixels>>,
|
||||
// pub(crate) element_offset_stack: Vec<Point<Pixels>>,
|
||||
// pub(crate) tooltip_request: Option<TooltipRequest>,
|
||||
// pub(crate) cursor_styles: FxHashMap<EntityId, CursorStyle>,
|
||||
// pub(crate) requested_cursor_style: Option<CursorStyle>,
|
||||
// pub(crate) view_stack: Vec<EntityId>,
|
||||
|
||||
#[cfg(any(test, feature = "test-support"))]
|
||||
debug_bounds: FxHashMap::default(),
|
||||
}
|
||||
}
|
||||
// #[cfg(any(test, feature = "test-support"))]
|
||||
// pub(crate) debug_bounds: FxHashMap<String, Bounds<Pixels>>,
|
||||
// }
|
||||
|
||||
pub(crate) fn clear(&mut self) {
|
||||
self.element_states.clear();
|
||||
self.mouse_listeners.values_mut().for_each(Vec::clear);
|
||||
self.dispatch_tree.clear();
|
||||
self.depth_map.clear();
|
||||
self.next_stacking_order_ids = vec![0];
|
||||
self.next_root_z_index = 0;
|
||||
self.reused_views.clear();
|
||||
self.scene.clear();
|
||||
self.requested_input_handler.take();
|
||||
self.tooltip_request.take();
|
||||
self.cursor_styles.clear();
|
||||
self.requested_cursor_style.take();
|
||||
debug_assert_eq!(self.view_stack.len(), 0);
|
||||
}
|
||||
// impl Frame {
|
||||
// pub(crate) fn new(dispatch_tree: DispatchTree) -> Self {
|
||||
// Frame {
|
||||
// focus: None,
|
||||
// window_active: false,
|
||||
// element_states: FxHashMap::default(),
|
||||
// mouse_listeners: FxHashMap::default(),
|
||||
// input_handlers: Vec::new(),
|
||||
// dispatch_tree,
|
||||
// scene: Scene::default(),
|
||||
// depth_map: Vec::new(),
|
||||
// z_index_stack: StackingOrder::default(),
|
||||
// next_stacking_order_ids: vec![0],
|
||||
// next_root_z_index: 0,
|
||||
// content_mask_stack: Vec::new(),
|
||||
// element_offset_stack: Vec::new(),
|
||||
// tooltip_request: None,
|
||||
// cursor_styles: FxHashMap::default(),
|
||||
// requested_cursor_style: None,
|
||||
// view_stack: Vec::new(),
|
||||
|
||||
pub(crate) fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
|
||||
self.focus
|
||||
.map(|focus_id| self.dispatch_tree.focus_path(focus_id))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
// #[cfg(any(test, feature = "test-support"))]
|
||||
// debug_bounds: FxHashMap::default(),
|
||||
// }
|
||||
// }
|
||||
|
||||
pub(crate) fn finish(&mut self, prev_frame: &mut Self) {
|
||||
// Reuse mouse listeners that didn't change since the last frame.
|
||||
for (type_id, listeners) in &mut prev_frame.mouse_listeners {
|
||||
let next_listeners = self.mouse_listeners.entry(*type_id).or_default();
|
||||
for (order, view_id, listener) in listeners.drain(..) {
|
||||
if self.reused_views.contains(&view_id) {
|
||||
next_listeners.push((order, view_id, listener));
|
||||
}
|
||||
}
|
||||
}
|
||||
// pub(crate) fn clear(&mut self) {
|
||||
// self.element_states.clear();
|
||||
// self.mouse_listeners.values_mut().for_each(Vec::clear);
|
||||
// self.input_handlers.clear();
|
||||
// self.dispatch_tree.clear();
|
||||
// self.depth_map.clear();
|
||||
// self.next_stacking_order_ids = vec![0];
|
||||
// self.next_root_z_index = 0;
|
||||
// self.scene.clear();
|
||||
// self.tooltip_request.take();
|
||||
// self.cursor_styles.clear();
|
||||
// self.requested_cursor_style.take();
|
||||
// debug_assert_eq!(self.view_stack.len(), 0);
|
||||
// }
|
||||
|
||||
// Reuse entries in the depth map that didn't change since the last frame.
|
||||
for (order, view_id, bounds) in prev_frame.depth_map.drain(..) {
|
||||
if self.reused_views.contains(&view_id) {
|
||||
match self
|
||||
.depth_map
|
||||
.binary_search_by(|(level, _, _)| order.cmp(level))
|
||||
{
|
||||
Ok(i) | Err(i) => self.depth_map.insert(i, (order, view_id, bounds)),
|
||||
}
|
||||
}
|
||||
}
|
||||
// pub(crate) fn focus_path(&self) -> SmallVec<[FocusId; 8]> {
|
||||
// self.focus
|
||||
// .map(|focus_id| self.dispatch_tree.focus_path(focus_id))
|
||||
// .unwrap_or_default()
|
||||
// }
|
||||
|
||||
// Retain element states for views that didn't change since the last frame.
|
||||
for (element_id, state) in prev_frame.element_states.drain() {
|
||||
if self.reused_views.contains(&state.parent_view_id) {
|
||||
self.element_states.entry(element_id).or_insert(state);
|
||||
}
|
||||
}
|
||||
// pub(crate) fn current_index(&self) -> FrameIndex {
|
||||
// FrameIndex {
|
||||
// scene_index: self.scene.current_index(),
|
||||
// mouse_listeners: self.mouse_listeners.len(),
|
||||
// dispatch_nodes: self.dispatch_tree.len(),
|
||||
// }
|
||||
// }
|
||||
|
||||
// Reuse geometry that didn't change since the last frame.
|
||||
self.scene
|
||||
.reuse_views(&self.reused_views, &mut prev_frame.scene);
|
||||
self.scene.finish();
|
||||
}
|
||||
}
|
||||
// pub(crate) fn finish(&mut self, prev_frame: &mut Self) {
|
||||
// // Reuse mouse listeners that didn't change since the last frame.
|
||||
// for (type_id, listeners) in &mut prev_frame.mouse_listeners {
|
||||
// let next_listeners = self.mouse_listeners.entry(*type_id).or_default();
|
||||
// for (order, view_id, listener) in listeners.drain(..) {
|
||||
// if self.reused_views.contains(&view_id) {
|
||||
// next_listeners.push((order, view_id, listener));
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Reuse entries in the depth map that didn't change since the last frame.
|
||||
// for (order, view_id, bounds) in prev_frame.depth_map.drain(..) {
|
||||
// if self.reused_views.contains(&view_id) {
|
||||
// match self
|
||||
// .depth_map
|
||||
// .binary_search_by(|(level, _, _)| order.cmp(level))
|
||||
// {
|
||||
// Ok(i) | Err(i) => self.depth_map.insert(i, (order, view_id, bounds)),
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// // Retain element states for views that didn't change since the last frame.
|
||||
// for (element_id, state) in prev_frame.element_states.drain() {
|
||||
// if self.reused_views.contains(&state.parent_view_id) {
|
||||
// self.element_states.entry(element_id).or_insert(state);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
/// This context is used for assisting in the implementation of the element trait
|
||||
#[derive(Deref, DerefMut)]
|
||||
@@ -310,8 +314,9 @@ impl<'a> VisualContext for ElementContext<'a> {
|
||||
}
|
||||
|
||||
impl<'a> ElementContext<'a> {
|
||||
pub(crate) fn reuse_view(&mut self, next_stacking_order_id: u16) {
|
||||
pub(crate) fn reuse_view(&mut self, subframe_range: Range<FrameIndex>) {
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let grafted_view_ids = self
|
||||
.cx
|
||||
.window
|
||||
@@ -651,6 +656,7 @@ impl<'a> ElementContext<'a> {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Paint one or more drop shadows into the scene for the next frame at the current z-index.
|
||||
pub fn paint_shadows(
|
||||
&mut self,
|
||||
@@ -666,20 +672,18 @@ impl<'a> ElementContext<'a> {
|
||||
let mut shadow_bounds = bounds;
|
||||
shadow_bounds.origin += shadow.offset;
|
||||
shadow_bounds.dilate(shadow.spread_radius);
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
Shadow {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
window
|
||||
.next_frame
|
||||
.scene
|
||||
.paint_primitive(|draw_order| Shadow {
|
||||
draw_order,
|
||||
bounds: shadow_bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
corner_radii: corner_radii.scale(scale_factor),
|
||||
color: shadow.color,
|
||||
blur_radius: shadow.blur_radius.scale(scale_factor),
|
||||
pad: 0,
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -692,20 +696,15 @@ impl<'a> ElementContext<'a> {
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
Quad {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
bounds: quad.bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
background: quad.background,
|
||||
border_color: quad.border_color,
|
||||
corner_radii: quad.corner_radii.scale(scale_factor),
|
||||
border_widths: quad.border_widths.scale(scale_factor),
|
||||
},
|
||||
);
|
||||
window.next_frame.scene.paint_primitive(|draw_order| Quad {
|
||||
draw_order,
|
||||
bounds: quad.bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
background: quad.background,
|
||||
border_color: quad.border_color,
|
||||
corner_radii: quad.corner_radii.scale(scale_factor),
|
||||
border_widths: quad.border_widths.scale(scale_factor),
|
||||
});
|
||||
}
|
||||
|
||||
/// Paint the given `Path` into the scene for the next frame at the current z-index.
|
||||
@@ -716,12 +715,11 @@ impl<'a> ElementContext<'a> {
|
||||
|
||||
path.content_mask = content_mask;
|
||||
path.color = color.into();
|
||||
path.view_id = view_id.into();
|
||||
let window = &mut *self.window;
|
||||
window
|
||||
.next_frame
|
||||
.scene
|
||||
.insert(&window.next_frame.z_index_stack, path.scale(scale_factor));
|
||||
window.next_frame.scene.paint_primitive(|draw_order| {
|
||||
path.draw_order = draw_order;
|
||||
path.scale(scale_factor)
|
||||
});
|
||||
}
|
||||
|
||||
/// Paint an underline into the scene for the next frame at the current z-index.
|
||||
@@ -745,19 +743,17 @@ impl<'a> ElementContext<'a> {
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
Underline {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
window
|
||||
.next_frame
|
||||
.scene
|
||||
.paint_primitive(|draw_order| Underline {
|
||||
draw_order,
|
||||
bounds: bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
color: style.color.unwrap_or_default(),
|
||||
thickness: style.thickness.scale(scale_factor),
|
||||
wavy: style.wavy,
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Paint a strikethrough into the scene for the next frame at the current z-index.
|
||||
@@ -777,19 +773,17 @@ impl<'a> ElementContext<'a> {
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
Underline {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
window
|
||||
.next_frame
|
||||
.scene
|
||||
.paint_primitive(|draw_order| Underline {
|
||||
draw_order,
|
||||
bounds: bounds.scale(scale_factor),
|
||||
content_mask: content_mask.scale(scale_factor),
|
||||
thickness: style.thickness.scale(scale_factor),
|
||||
color: style.color.unwrap_or_default(),
|
||||
wavy: false,
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
/// Paints a monochrome (non-emoji) glyph into the scene for the next frame at the current z-index.
|
||||
@@ -837,18 +831,16 @@ impl<'a> ElementContext<'a> {
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
MonochromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
window
|
||||
.next_frame
|
||||
.scene
|
||||
.paint_primitive(|draw_order| MonochromeSprite {
|
||||
draw_order,
|
||||
bounds,
|
||||
content_mask,
|
||||
color,
|
||||
tile,
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -895,20 +887,18 @@ impl<'a> ElementContext<'a> {
|
||||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
PolychromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
window
|
||||
.next_frame
|
||||
.scene
|
||||
.paint_primitive(|draw_order| PolychromeSprite {
|
||||
draw_order,
|
||||
bounds,
|
||||
corner_radii: Default::default(),
|
||||
content_mask,
|
||||
tile,
|
||||
grayscale: false,
|
||||
pad: 0,
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -941,18 +931,16 @@ impl<'a> ElementContext<'a> {
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
MonochromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
window
|
||||
.next_frame
|
||||
.scene
|
||||
.paint_primitive(|draw_order| MonochromeSprite {
|
||||
draw_order,
|
||||
bounds,
|
||||
content_mask,
|
||||
color,
|
||||
tile,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -980,20 +968,18 @@ impl<'a> ElementContext<'a> {
|
||||
let view_id = self.parent_view_id();
|
||||
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
PolychromeSprite {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
window
|
||||
.next_frame
|
||||
.scene
|
||||
.paint_primitive(|draw_order| PolychromeSprite {
|
||||
draw_order,
|
||||
bounds,
|
||||
content_mask,
|
||||
corner_radii,
|
||||
tile,
|
||||
grayscale,
|
||||
pad: 0,
|
||||
},
|
||||
);
|
||||
});
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1005,17 +991,15 @@ impl<'a> ElementContext<'a> {
|
||||
let content_mask = self.content_mask().scale(scale_factor);
|
||||
let view_id = self.parent_view_id();
|
||||
let window = &mut *self.window;
|
||||
window.next_frame.scene.insert(
|
||||
&window.next_frame.z_index_stack,
|
||||
crate::Surface {
|
||||
view_id: view_id.into(),
|
||||
layer_id: 0,
|
||||
order: 0,
|
||||
window
|
||||
.next_frame
|
||||
.scene
|
||||
.paint_primitive(|draw_order| crate::Surface {
|
||||
draw_order,
|
||||
bounds,
|
||||
content_mask,
|
||||
image_buffer,
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
use editor::Editor;
|
||||
use gpui::{
|
||||
div, list, prelude::*, uniform_list, AnyElement, AppContext, DismissEvent, EventEmitter,
|
||||
FocusHandle, FocusableView, Length, ListState, MouseButton, MouseDownEvent, Render, Task,
|
||||
div, list, prelude::*, uniform_list, AnyElement, AppContext, ClickEvent, DismissEvent,
|
||||
EventEmitter, FocusHandle, FocusableView, Length, ListState, Render, Task,
|
||||
UniformListScrollHandle, View, ViewContext, WindowContext,
|
||||
};
|
||||
use std::{sync::Arc, time::Duration};
|
||||
@@ -103,7 +103,7 @@ impl<D: PickerDelegate> Picker<D> {
|
||||
let mut this = Self {
|
||||
delegate,
|
||||
editor,
|
||||
element_container: Self::crate_element_container(is_uniform, cx),
|
||||
element_container: Self::create_element_container(is_uniform, cx),
|
||||
pending_update_matches: None,
|
||||
confirm_on_update: None,
|
||||
width: None,
|
||||
@@ -117,7 +117,7 @@ impl<D: PickerDelegate> Picker<D> {
|
||||
this
|
||||
}
|
||||
|
||||
fn crate_element_container(is_uniform: bool, cx: &mut ViewContext<Self>) -> ElementContainer {
|
||||
fn create_element_container(is_uniform: bool, cx: &mut ViewContext<Self>) -> ElementContainer {
|
||||
if is_uniform {
|
||||
ElementContainer::UniformList(UniformListScrollHandle::new())
|
||||
} else {
|
||||
@@ -311,12 +311,10 @@ impl<D: PickerDelegate> Picker<D> {
|
||||
|
||||
fn render_element(&self, cx: &mut ViewContext<Self>, ix: usize) -> impl IntoElement {
|
||||
div()
|
||||
.on_mouse_down(
|
||||
MouseButton::Left,
|
||||
cx.listener(move |this, event: &MouseDownEvent, cx| {
|
||||
this.handle_click(ix, event.modifiers.command, cx)
|
||||
}),
|
||||
)
|
||||
.id(("item", ix))
|
||||
.on_click(cx.listener(move |this, event: &ClickEvent, cx| {
|
||||
this.handle_click(ix, event.down.modifiers.command, cx)
|
||||
}))
|
||||
.children(
|
||||
self.delegate
|
||||
.render_match(ix, ix == self.delegate.selected_index(), cx),
|
||||
|
||||
@@ -3,16 +3,16 @@ mod projects;
|
||||
|
||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||
use gpui::{
|
||||
AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Result, Subscription, Task,
|
||||
View, ViewContext, WeakView,
|
||||
AnyElement, AppContext, DismissEvent, EventEmitter, FocusHandle, FocusableView, Result,
|
||||
Subscription, Task, View, ViewContext, WeakView,
|
||||
};
|
||||
use highlighted_workspace_location::HighlightedWorkspaceLocation;
|
||||
use ordered_float::OrderedFloat;
|
||||
use picker::{Picker, PickerDelegate};
|
||||
use std::sync::Arc;
|
||||
use ui::{prelude::*, tooltip_container, HighlightedLabel, ListItem, ListItemSpacing};
|
||||
use ui::{prelude::*, tooltip_container, HighlightedLabel, ListItem, ListItemSpacing, Tooltip};
|
||||
use util::paths::PathExt;
|
||||
use workspace::{ModalView, Workspace, WorkspaceLocation, WORKSPACE_DB};
|
||||
use workspace::{ModalView, Workspace, WorkspaceId, WorkspaceLocation, WORKSPACE_DB};
|
||||
|
||||
pub use projects::OpenRecent;
|
||||
|
||||
@@ -45,13 +45,11 @@ impl RecentProjects {
|
||||
let workspaces = WORKSPACE_DB
|
||||
.recent_workspaces_on_disk()
|
||||
.await
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|(_, location)| location)
|
||||
.collect();
|
||||
.unwrap_or_default();
|
||||
|
||||
this.update(&mut cx, move |this, cx| {
|
||||
this.picker.update(cx, move |picker, cx| {
|
||||
picker.delegate.workspace_locations = workspaces;
|
||||
picker.delegate.workspaces = workspaces;
|
||||
picker.update_matches(picker.query(cx), cx)
|
||||
})
|
||||
})
|
||||
@@ -124,20 +122,23 @@ impl Render for RecentProjects {
|
||||
|
||||
pub struct RecentProjectsDelegate {
|
||||
workspace: WeakView<Workspace>,
|
||||
workspace_locations: Vec<WorkspaceLocation>,
|
||||
workspaces: Vec<(WorkspaceId, WorkspaceLocation)>,
|
||||
selected_match_index: usize,
|
||||
matches: Vec<StringMatch>,
|
||||
render_paths: bool,
|
||||
// Flag to reset index when there is a new query vs not reset index when user delete an item
|
||||
reset_selected_match_index: bool,
|
||||
}
|
||||
|
||||
impl RecentProjectsDelegate {
|
||||
fn new(workspace: WeakView<Workspace>, render_paths: bool) -> Self {
|
||||
Self {
|
||||
workspace,
|
||||
workspace_locations: vec![],
|
||||
workspaces: vec![],
|
||||
selected_match_index: 0,
|
||||
matches: Default::default(),
|
||||
render_paths,
|
||||
reset_selected_match_index: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -169,10 +170,10 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||
let query = query.trim_start();
|
||||
let smart_case = query.chars().any(|c| c.is_uppercase());
|
||||
let candidates = self
|
||||
.workspace_locations
|
||||
.workspaces
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(id, location)| {
|
||||
.map(|(id, (_, location))| {
|
||||
let combined_string = location
|
||||
.paths()
|
||||
.iter()
|
||||
@@ -192,14 +193,17 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||
));
|
||||
self.matches.sort_unstable_by_key(|m| m.candidate_id);
|
||||
|
||||
self.selected_match_index = self
|
||||
.matches
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev()
|
||||
.max_by_key(|(_, m)| OrderedFloat(m.score))
|
||||
.map(|(ix, _)| ix)
|
||||
.unwrap_or(0);
|
||||
if self.reset_selected_match_index {
|
||||
self.selected_match_index = self
|
||||
.matches
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev()
|
||||
.max_by_key(|(_, m)| OrderedFloat(m.score))
|
||||
.map(|(ix, _)| ix)
|
||||
.unwrap_or(0);
|
||||
}
|
||||
self.reset_selected_match_index = true;
|
||||
Task::ready(())
|
||||
}
|
||||
|
||||
@@ -209,7 +213,7 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||
.get(self.selected_index())
|
||||
.zip(self.workspace.upgrade())
|
||||
{
|
||||
let workspace_location = &self.workspace_locations[selected_match.candidate_id];
|
||||
let (_, workspace_location) = &self.workspaces[selected_match.candidate_id];
|
||||
workspace
|
||||
.update(cx, |workspace, cx| {
|
||||
workspace
|
||||
@@ -226,19 +230,18 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||
&self,
|
||||
ix: usize,
|
||||
selected: bool,
|
||||
_cx: &mut ViewContext<Picker<Self>>,
|
||||
cx: &mut ViewContext<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let Some(r#match) = self.matches.get(ix) else {
|
||||
return None;
|
||||
};
|
||||
|
||||
let highlighted_location = HighlightedWorkspaceLocation::new(
|
||||
&r#match,
|
||||
&self.workspace_locations[r#match.candidate_id],
|
||||
);
|
||||
|
||||
let (workspace_id, location) = &self.workspaces[r#match.candidate_id];
|
||||
let highlighted_location: HighlightedWorkspaceLocation =
|
||||
HighlightedWorkspaceLocation::new(&r#match, location);
|
||||
let tooltip_highlighted_location = highlighted_location.clone();
|
||||
|
||||
let is_current_workspace = self.is_current_workspace(*workspace_id, cx);
|
||||
Some(
|
||||
ListItem::new(ix)
|
||||
.inset(true)
|
||||
@@ -255,6 +258,27 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||
}))
|
||||
}),
|
||||
)
|
||||
.when(!is_current_workspace, |el| {
|
||||
let delete_button = div()
|
||||
.child(
|
||||
IconButton::new("delete", IconName::Close)
|
||||
.icon_size(IconSize::Small)
|
||||
.on_click(cx.listener(move |this, _event, cx| {
|
||||
cx.stop_propagation();
|
||||
cx.prevent_default();
|
||||
|
||||
this.delegate.delete_recent_project(ix, cx)
|
||||
}))
|
||||
.tooltip(|cx| Tooltip::text("Delete From Recent Projects...", cx)),
|
||||
)
|
||||
.into_any_element();
|
||||
|
||||
if self.selected_index() == ix {
|
||||
el.end_slot::<AnyElement>(delete_button)
|
||||
} else {
|
||||
el.end_hover_slot::<AnyElement>(delete_button)
|
||||
}
|
||||
})
|
||||
.tooltip(move |cx| {
|
||||
let tooltip_highlighted_location = tooltip_highlighted_location.clone();
|
||||
cx.new_view(move |_| MatchTooltip {
|
||||
@@ -266,6 +290,42 @@ impl PickerDelegate for RecentProjectsDelegate {
|
||||
}
|
||||
}
|
||||
|
||||
impl RecentProjectsDelegate {
|
||||
fn delete_recent_project(&self, ix: usize, cx: &mut ViewContext<Picker<Self>>) {
|
||||
if let Some(selected_match) = self.matches.get(ix) {
|
||||
let (workspace_id, _) = self.workspaces[selected_match.candidate_id];
|
||||
cx.spawn(move |this, mut cx| async move {
|
||||
let _ = WORKSPACE_DB.delete_workspace_by_id(workspace_id).await;
|
||||
let workspaces = WORKSPACE_DB
|
||||
.recent_workspaces_on_disk()
|
||||
.await
|
||||
.unwrap_or_default();
|
||||
this.update(&mut cx, move |picker, cx| {
|
||||
picker.delegate.workspaces = workspaces;
|
||||
picker.delegate.set_selected_index(ix - 1, cx);
|
||||
picker.delegate.reset_selected_match_index = false;
|
||||
picker.update_matches(picker.query(cx), cx)
|
||||
})
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
}
|
||||
|
||||
fn is_current_workspace(
|
||||
&self,
|
||||
workspace_id: WorkspaceId,
|
||||
cx: &mut ViewContext<Picker<Self>>,
|
||||
) -> bool {
|
||||
if let Some(workspace) = self.workspace.upgrade() {
|
||||
let workspace = workspace.read(cx);
|
||||
if workspace_id == workspace.database_id() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
struct MatchTooltip {
|
||||
highlighted_location: HighlightedWorkspaceLocation,
|
||||
}
|
||||
|
||||
@@ -430,7 +430,7 @@ impl WorkspaceDb {
|
||||
}
|
||||
|
||||
query! {
|
||||
async fn delete_stale_workspace(id: WorkspaceId) -> Result<()> {
|
||||
pub async fn delete_workspace_by_id(id: WorkspaceId) -> Result<()> {
|
||||
DELETE FROM workspaces
|
||||
WHERE workspace_id IS ?
|
||||
}
|
||||
@@ -447,7 +447,7 @@ impl WorkspaceDb {
|
||||
{
|
||||
result.push((id, location));
|
||||
} else {
|
||||
delete_tasks.push(self.delete_stale_workspace(id));
|
||||
delete_tasks.push(self.delete_workspace_by_id(id));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
description = "The fast, collaborative code editor."
|
||||
edition = "2021"
|
||||
name = "zed"
|
||||
version = "0.124.0"
|
||||
version = "0.125.0"
|
||||
publish = false
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@ These docs are intendended to replace both docs.zed.dev and introduce people to
|
||||
|
||||
1. `cd docs` from repo root
|
||||
1. Install the vercel cli if you haven't already
|
||||
- `pnpm i -g vercel`
|
||||
- `pnpm i -g vercel`
|
||||
1. `vercel` to deploy if you already have the project linked
|
||||
1. Otherwise, `vercel login` and `vercel` to link
|
||||
- Choose Zed Industries as the team, then `zed-app-docs` as the project
|
||||
- Choose Zed Industries as the team, then `zed-app-docs` as the project
|
||||
|
||||
Someone can write a script for this when they have time.
|
||||
|
||||
@@ -307,6 +307,7 @@ To override settings for a language, add an entry for that language server's nam
|
||||
```
|
||||
|
||||
## Auto close
|
||||
|
||||
- Description: Whether or not to automatically type closing characters for you.
|
||||
- Setting: `use_autoclose`
|
||||
- Default: `true`
|
||||
|
||||
@@ -8,7 +8,6 @@ A selection of base keymaps is available in the welcome screen under the "Choose
|
||||
|
||||
Additionally, you can change the base keymap from the command palette - `⌘-Shift-P` - by selecting the "welcome: toggle base keymap selector" command.
|
||||
|
||||
|
||||
## Custom key bindings
|
||||
|
||||
### Accessing custom key bindings
|
||||
@@ -39,24 +38,25 @@ You can see more examples in Zed's [`default.json`](https://zed.dev/ref/default.
|
||||
_There are some key bindings that can't be overridden; we are working on an issue surrounding this._
|
||||
|
||||
## Special Keyboard Layouts
|
||||
|
||||
Some people have unique and custom keyboard layouts.
|
||||
|
||||
For example, [@TomPlanche](https://github.com/TomPlanche) having a [French keyboard](https%3A%2F%2Fcdn.shopify.com%2Fs%2Ffiles%2F1%2F0810%2F3669%2Ffiles%2Ffrench-azerty-mac-keyboard-layout-2021-keyshorts.png&f=1&nofb=1&ipt=f53a06c5e60a20b621082410aa699c8cceff269a11ff90b3b5a35c6124dbf827&ipo=images), had to type `Shift-Alt-(` in order to have a simple `[` so he made a simple layout with those 'rules':
|
||||
`ù -> [`, `backtick -> ]`, `Alt-[ (where [ is the old ù) -> {`, `Alt-] -> }`.
|
||||
But, it was impossible to take into account the `{` and `}` when he was typing so now, in order to ignore a binding, he can add `null` to the binding:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"context": "Editor",
|
||||
"bindings": {
|
||||
"alt-[": null,
|
||||
"alt-]": null
|
||||
}
|
||||
{
|
||||
"context": "Editor",
|
||||
"bindings": {
|
||||
"alt-[": null,
|
||||
"alt-]": null
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
|
||||
## All key bindings
|
||||
|
||||
### Global
|
||||
|
||||
@@ -26,7 +26,6 @@ git submodule update --init --recursive
|
||||
|
||||
- If you prefer to install the system libraries manually, you can find the list of required packages in the `script/linux` file.
|
||||
|
||||
|
||||
## Backend Dependencies
|
||||
|
||||
# Note: This section is still in development. The instructions are not yet complete.
|
||||
|
||||
@@ -22,6 +22,7 @@ Zed sets the following initialization options for inlay hints:
|
||||
to make the language server send back inlay hints when Zed has them enabled in the settings.
|
||||
|
||||
Use
|
||||
|
||||
```json
|
||||
"lsp": {
|
||||
"$LANGUAGE_SERVER_NAME": {
|
||||
@@ -33,6 +34,7 @@ Use
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
to override these settings.
|
||||
|
||||
See https://github.com/golang/tools/blob/master/gopls/doc/inlayHints.md for more information.
|
||||
@@ -49,5 +51,5 @@ TODO: https://github.com/zed-industries/zed/pull/7139
|
||||
# Go Work
|
||||
|
||||
- Tree Sitter:
|
||||
[tree-sitter-go-work](https://github.com/d1y/tree-sitter-go-work)
|
||||
[tree-sitter-go-work](https://github.com/d1y/tree-sitter-go-work)
|
||||
- Language Server: N/A
|
||||
|
||||
@@ -4,14 +4,17 @@
|
||||
- Language Server: [ocamllsp](https://github.com/ocaml/ocaml-lsp)
|
||||
|
||||
## Setup Instructions
|
||||
|
||||
If you have the development environment already setup, you can skip to [Launching Zed](#launching-zed)
|
||||
|
||||
### Using OPAM
|
||||
Opam is the official package manager for OCaml and is highly recommended for getting started with OCaml. To get started using Opam, please follow the instructions provided [here](https://ocaml.org/install).
|
||||
|
||||
Opam is the official package manager for OCaml and is highly recommended for getting started with OCaml. To get started using Opam, please follow the instructions provided [here](https://ocaml.org/install).
|
||||
|
||||
Once you install opam and setup a switch with your development environment as per the instructions, you can proceed.
|
||||
|
||||
### Launching Zed
|
||||
|
||||
By now you should have `ocamllsp` installed, you can verify so by running
|
||||
|
||||
```sh
|
||||
@@ -22,7 +25,7 @@ in your terminal. If you get a help message, you're good to go. If not, please r
|
||||
|
||||
With that aside, we can now launch Zed. Given how the OCaml package manager works, we require you to run Zed from the terminal, so please make sure you install the [Zed cli](https://zed.dev/features#cli) if you haven't already.
|
||||
|
||||
Once you have the cli, simply from a terminal, navigate to your project and run
|
||||
Once you have the cli, simply from a terminal, navigate to your project and run
|
||||
|
||||
```sh
|
||||
$ zed .
|
||||
|
||||
@@ -51,6 +51,7 @@ Use
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
to override these settings.
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ Zed sets the following initialization options for inlay hints:
|
||||
to make the language server send back inlay hints when Zed has them enabled in the settings.
|
||||
|
||||
Use
|
||||
|
||||
```json
|
||||
"lsp": {
|
||||
"$LANGUAGE_SERVER_NAME": {
|
||||
@@ -34,6 +35,7 @@ Use
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
to override these settings.
|
||||
|
||||
See https://github.com/typescript-language-server/typescript-language-server?tab=readme-ov-file#inlay-hints-textdocumentinlayhint for more information.
|
||||
|
||||
19
script/linux
19
script/linux
@@ -13,6 +13,7 @@ if [[ -n $apt ]]; then
|
||||
vulkan-validationlayers*
|
||||
libwayland-dev
|
||||
libxkbcommon-x11-dev
|
||||
openssl
|
||||
)
|
||||
$maysudo "$apt" install -y "${deps[@]}"
|
||||
exit 0
|
||||
@@ -28,11 +29,28 @@ if [[ -n $dnf ]]; then
|
||||
vulkan-validation-layers
|
||||
wayland-devel
|
||||
libxkbcommon-x11-devel
|
||||
openssl-devel
|
||||
)
|
||||
$maysudo "$dnf" install -y "${deps[@]}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# openSuse
|
||||
# https://software.opensuse.org/
|
||||
zyp=$(command -v zypper || true)
|
||||
if [[ -n $zyp ]]; then
|
||||
deps=(
|
||||
alsa-devel
|
||||
fontconfig-devel
|
||||
vulkan-validationlayers
|
||||
wayland-devel
|
||||
libxkbcommon-x11-devel
|
||||
openssl-devel
|
||||
)
|
||||
$maysudo "$zyp" install -y "${deps[@]}"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Arch, Manjaro, etc.
|
||||
# https://archlinux.org/packages
|
||||
pacman=$(command -v pacman || true)
|
||||
@@ -43,6 +61,7 @@ if [[ -n $pacman ]]; then
|
||||
vulkan-validation-layers
|
||||
wayland
|
||||
libxkbcommon-x11
|
||||
openssl
|
||||
)
|
||||
$maysudo "$pacman" -S --needed --noconfirm "${deps[@]}"
|
||||
exit 0
|
||||
|
||||
Reference in New Issue
Block a user