Merge branch 'main' into bounds-for-range

This commit is contained in:
Max Brunsfeld 2023-11-08 17:30:06 -08:00
commit 4350801399
24 changed files with 534 additions and 338 deletions

1
Cargo.lock generated
View file

@ -11352,6 +11352,7 @@ dependencies = [
"libc",
"log",
"lsp2",
"menu2",
"node_runtime",
"num_cpus",
"parking_lot 0.11.2",

View file

@ -39,11 +39,10 @@ use futures::FutureExt;
use fuzzy::{StringMatch, StringMatchCandidate};
use git::diff_hunk_to_display;
use gpui::{
action, actions, div, point, px, relative, size, AnyElement, AppContext, BackgroundExecutor,
Bounds, ClipboardItem, Context, DispatchContext, Div, Element, Entity, EventEmitter,
FocusHandle, FontStyle, FontWeight, HighlightStyle, Hsla, InputHandler, Model, Pixels,
PlatformInputHandler, Render, Styled, Subscription, Task, TextStyle, View, ViewContext,
VisualContext, WeakView, WindowContext,
action, actions, point, px, relative, rems, size, AnyElement, AppContext, BackgroundExecutor,
Bounds, ClipboardItem, Context, DispatchContext, EventEmitter, FocusHandle, FontFeatures,
FontStyle, FontWeight, HighlightStyle, Hsla, InputHandler, Model, Pixels, Render, Subscription,
Task, TextStyle, View, ViewContext, VisualContext, WeakView, WindowContext,
};
use highlight_matching_bracket::refresh_matching_bracket_highlights;
use hover_popover::{hide_hover, HoverState};
@ -57,6 +56,7 @@ use language::{
Diagnostic, IndentKind, IndentSize, Language, LanguageRegistry, LanguageServerName,
OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId,
};
use lazy_static::lazy_static;
use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState};
use lsp::{DiagnosticSeverity, Documentation, LanguageServerId};
use movement::TextLayoutDetails;
@ -66,7 +66,7 @@ pub use multi_buffer::{
ToPoint,
};
use ordered_float::OrderedFloat;
use parking_lot::RwLock;
use parking_lot::{Mutex, RwLock};
use project::{FormatTrigger, Location, Project};
use rand::prelude::*;
use rpc::proto::*;
@ -2180,14 +2180,14 @@ impl Editor {
// self.collaboration_hub = Some(hub);
// }
// pub fn set_placeholder_text(
// &mut self,
// placeholder_text: impl Into<Arc<str>>,
// cx: &mut ViewContext<Self>,
// ) {
// self.placeholder_text = Some(placeholder_text.into());
// cx.notify();
// }
pub fn set_placeholder_text(
&mut self,
placeholder_text: impl Into<Arc<str>>,
cx: &mut ViewContext<Self>,
) {
self.placeholder_text = Some(placeholder_text.into());
cx.notify();
}
// pub fn set_cursor_shape(&mut self, cursor_shape: CursorShape, cx: &mut ViewContext<Self>) {
// self.cursor_shape = cursor_shape;
@ -9418,18 +9418,42 @@ impl Render for Editor {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let settings = ThemeSettings::get_global(cx);
let text_style = TextStyle {
color: cx.theme().colors().text,
font_family: settings.buffer_font.family.clone(),
font_features: settings.buffer_font.features,
font_size: settings.buffer_font_size.into(),
font_weight: FontWeight::NORMAL,
font_style: FontStyle::Normal,
line_height: relative(settings.buffer_line_height.value()),
underline: None,
let text_style = match self.mode {
EditorMode::SingleLine => {
TextStyle {
color: cx.theme().colors().text,
font_family: "Zed Sans".into(), // todo!()
font_features: FontFeatures::default(),
font_size: rems(1.0).into(),
font_weight: FontWeight::NORMAL,
font_style: FontStyle::Normal,
line_height: relative(1.3).into(), // TODO relative(settings.buffer_line_height.value()),
underline: None,
}
}
EditorMode::AutoHeight { max_lines } => todo!(),
EditorMode::Full => TextStyle {
color: cx.theme().colors().text,
font_family: settings.buffer_font.family.clone(),
font_features: settings.buffer_font.features,
font_size: settings.buffer_font_size.into(),
font_weight: FontWeight::NORMAL,
font_style: FontStyle::Normal,
line_height: relative(settings.buffer_line_height.value()),
underline: None,
},
};
let background = match self.mode {
EditorMode::SingleLine => cx.theme().system().transparent,
EditorMode::AutoHeight { max_lines } => cx.theme().system().transparent,
EditorMode::Full => cx.theme().colors().editor_background,
};
EditorElement::new(EditorStyle {
background: cx.theme().colors().editor_background,
background,
local_player: cx.theme().players().local(),
text: text_style,
scrollbar_width: px(12.),

View file

@ -1,220 +1,195 @@
use gpui::{actions, div, px, red, AppContext, Div, Render, Styled, ViewContext, VisualContext};
use workspace::ModalRegistry;
use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor};
use gpui::{
actions, div, AppContext, Div, EventEmitter, ParentElement, Render, SharedString,
StatefulInteractivity, StatelessInteractive, Styled, Subscription, View, ViewContext,
VisualContext, WindowContext,
};
use text::{Bias, Point};
use theme::ActiveTheme;
use ui::{h_stack, modal, v_stack, Label, LabelColor};
use util::paths::FILE_ROW_COLUMN_DELIMITER;
use workspace::{Modal, ModalEvent, Workspace};
actions!(Toggle);
pub fn init(cx: &mut AppContext) {
cx.global_mut::<ModalRegistry>()
.register_modal(Toggle, |_, cx| {
// if let Some(editor) = workspace
// .active_item(cx)
// .and_then(|active_item| active_item.downcast::<Editor>())
// {
// cx.build_view(|cx| GoToLine::new(editor, cx))
// }
let view = cx.build_view(|_| GoToLine);
view
});
cx.observe_new_views(
|workspace: &mut Workspace, _: &mut ViewContext<Workspace>| {
workspace
.modal_layer()
.register_modal(Toggle, |workspace, cx| {
let editor = workspace
.active_item(cx)
.and_then(|active_item| active_item.downcast::<Editor>())?;
// cx.add_action(GoToLine::toggle);
// cx.add_action(GoToLine::confirm);
// cx.add_action(GoToLine::cancel);
Some(cx.build_view(|cx| GoToLine::new(editor, cx)))
});
},
)
.detach();
}
pub struct GoToLine;
pub struct GoToLine {
line_editor: View<Editor>,
active_editor: View<Editor>,
current_text: SharedString,
prev_scroll_position: Option<gpui::Point<f32>>,
_subscriptions: Vec<Subscription>,
}
impl Render for GoToLine {
type Element = Div<Self>;
pub enum Event {
Dismissed,
}
fn render(&mut self, _cx: &mut ViewContext<Self>) -> Self::Element {
div().bg(red()).w(px(100.0)).h(px(100.0))
impl EventEmitter for GoToLine {
type Event = Event;
}
impl GoToLine {
pub fn new(active_editor: View<Editor>, cx: &mut ViewContext<Self>) -> Self {
let line_editor = cx.build_view(|cx| {
let editor = Editor::single_line(cx);
editor.focus(cx);
editor
});
let line_editor_change = cx.subscribe(&line_editor, Self::on_line_editor_event);
let editor = active_editor.read(cx);
let cursor = editor.selections.last::<Point>(cx).head();
let last_line = editor.buffer().read(cx).snapshot(cx).max_point().row;
let scroll_position = active_editor.update(cx, |editor, cx| editor.scroll_position(cx));
let current_text = format!(
"line {} of {} (column {})",
cursor.row + 1,
last_line + 1,
cursor.column + 1,
);
Self {
line_editor,
active_editor,
current_text: current_text.into(),
prev_scroll_position: Some(scroll_position),
_subscriptions: vec![line_editor_change, cx.on_release(Self::release)],
}
}
fn release(&mut self, cx: &mut WindowContext) {
let scroll_position = self.prev_scroll_position.take();
self.active_editor.update(cx, |editor, cx| {
editor.focus(cx);
editor.highlight_rows(None);
if let Some(scroll_position) = scroll_position {
editor.set_scroll_position(scroll_position, cx);
}
cx.notify();
})
}
fn on_line_editor_event(
&mut self,
_: View<Editor>,
event: &editor::Event,
cx: &mut ViewContext<Self>,
) {
match event {
// todo!() this isn't working...
editor::Event::Blurred => cx.emit(Event::Dismissed),
editor::Event::BufferEdited { .. } => self.highlight_current_line(cx),
_ => {}
}
}
fn highlight_current_line(&mut self, cx: &mut ViewContext<Self>) {
if let Some(point) = self.point_from_query(cx) {
self.active_editor.update(cx, |active_editor, cx| {
let snapshot = active_editor.snapshot(cx).display_snapshot;
let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
let display_point = point.to_display_point(&snapshot);
let row = display_point.row();
active_editor.highlight_rows(Some(row..row + 1));
active_editor.request_autoscroll(Autoscroll::center(), cx);
});
cx.notify();
}
}
fn point_from_query(&self, cx: &ViewContext<Self>) -> Option<Point> {
let line_editor = self.line_editor.read(cx).text(cx);
let mut components = line_editor
.splitn(2, FILE_ROW_COLUMN_DELIMITER)
.map(str::trim)
.fuse();
let row = components.next().and_then(|row| row.parse::<u32>().ok())?;
let column = components.next().and_then(|col| col.parse::<u32>().ok());
Some(Point::new(
row.saturating_sub(1),
column.unwrap_or(0).saturating_sub(1),
))
}
fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
cx.emit(Event::Dismissed);
}
fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
if let Some(point) = self.point_from_query(cx) {
self.active_editor.update(cx, |active_editor, cx| {
let snapshot = active_editor.snapshot(cx).display_snapshot;
let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
active_editor.change_selections(Some(Autoscroll::center()), cx, |s| {
s.select_ranges([point..point])
});
});
self.prev_scroll_position.take();
}
cx.emit(Event::Dismissed);
}
}
// pub struct GoToLine {
// //line_editor: View<Editor>,
// active_editor: View<Editor>,
// prev_scroll_position: Option<gpui::Point<Pixels>>,
// cursor_point: Point,
// max_point: Point,
// has_focus: bool,
// }
impl Modal for GoToLine {
fn to_modal_event(&self, e: &Self::Event) -> Option<ModalEvent> {
match e {
Event::Dismissed => Some(ModalEvent::Dismissed),
}
}
}
// pub enum Event {
// Dismissed,
// }
impl Render for GoToLine {
type Element = Div<Self, StatefulInteractivity<Self>>;
// impl GoToLine {
// pub fn new(active_editor: View<Editor>, cx: &mut ViewContext<Self>) -> Self {
// // let line_editor = cx.build_view(|cx| {
// // Editor::single_line(
// // Some(Arc::new(|theme| theme.picker.input_editor.clone())),
// // cx,
// // )
// // });
// // cx.subscribe(&line_editor, Self::on_line_editor_event)
// // .detach();
// let (scroll_position, cursor_point, max_point) = active_editor.update(cx, |editor, cx| {
// let scroll_position = editor.scroll_position(cx);
// let buffer = editor.buffer().read(cx).snapshot(cx);
// (
// Some(scroll_position),
// editor.selections.newest(cx).head(),
// buffer.max_point(),
// )
// });
// cx.on_release(|_, on_release| {}).detach();
// Self {
// //line_editor,
// active_editor,
// prev_scroll_position: scroll_position,
// cursor_point,
// max_point,
// has_focus: false,
// }
// }
// fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
// cx.emit(Event::Dismissed);
// }
// fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
// self.prev_scroll_position.take();
// if let Some(point) = self.point_from_query(cx) {
// self.active_editor.update(cx, |active_editor, cx| {
// let snapshot = active_editor.snapshot(cx).display_snapshot;
// let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
// active_editor.change_selections(Some(Autoscroll::center()), cx, |s| {
// s.select_ranges([point..point])
// });
// });
// }
// cx.emit(Event::Dismissed);
// }
// fn on_line_editor_event(
// &mut self,
// _: View<Editor>,
// event: &editor::Event,
// cx: &mut ViewContext<Self>,
// ) {
// match event {
// editor::Event::Blurred => cx.emit(Event::Dismissed),
// editor::Event::BufferEdited { .. } => {
// if let Some(point) = self.point_from_query(cx) {
// // todo!()
// // self.active_editor.update(cx, |active_editor, cx| {
// // let snapshot = active_editor.snapshot(cx).display_snapshot;
// // let point = snapshot.buffer_snapshot.clip_point(point, Bias::Left);
// // let display_point = point.to_display_point(&snapshot);
// // let row = display_point.row();
// // active_editor.highlight_rows(Some(row..row + 1));
// // active_editor.request_autoscroll(Autoscroll::center(), cx);
// // });
// cx.notify();
// }
// }
// _ => {}
// }
// }
// fn point_from_query(&self, cx: &ViewContext<Self>) -> Option<Point> {
// return None;
// // todo!()
// // let line_editor = self.line_editor.read(cx).text(cx);
// // let mut components = line_editor
// // .splitn(2, FILE_ROW_COLUMN_DELIMITER)
// // .map(str::trim)
// // .fuse();
// // let row = components.next().and_then(|row| row.parse::<u32>().ok())?;
// // let column = components.next().and_then(|col| col.parse::<u32>().ok());
// // Some(Point::new(
// // row.saturating_sub(1),
// // column.unwrap_or(0).saturating_sub(1),
// // ))
// }
// }
// impl EventEmitter for GoToLine {
// type Event = Event;
// }
// impl Entity for GoToLine {
// fn release(&mut self, cx: &mut AppContext) {
// let scroll_position = self.prev_scroll_position.take();
// self.active_editor.window().update(cx, |cx| {
// self.active_editor.update(cx, |editor, cx| {
// editor.highlight_rows(None);
// if let Some(scroll_position) = scroll_position {
// editor.set_scroll_position(scroll_position, cx);
// }
// })
// });
// }
// }
// impl Render for GoToLine {
// type Element = Div<Self>;
// fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
// // todo!()
// div()
// }
// }
// impl View for GoToLine {
// fn ui_name() -> &'static str {
// "GoToLine"
// }
// fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
// let theme = &theme::current(cx).picker;
// let label = format!(
// "{}{FILE_ROW_COLUMN_DELIMITER}{} of {} lines",
// self.cursor_point.row + 1,
// self.cursor_point.column + 1,
// self.max_point.row + 1
// );
// Flex::new(Axis::Vertical)
// .with_child(
// ChildView::new(&self.line_editor, cx)
// .contained()
// .with_style(theme.input_editor.container),
// )
// .with_child(
// Label::new(label, theme.no_matches.label.clone())
// .contained()
// .with_style(theme.no_matches.container),
// )
// .contained()
// .with_style(theme.container)
// .constrained()
// .with_max_width(500.0)
// .into_any_named("go to line")
// }
// fn focus_in(&mut self, _: AnyView, cx: &mut ViewContext<Self>) {
// self.has_focus = true;
// cx.focus(&self.line_editor);
// }
// fn focus_out(&mut self, _: AnyView, _: &mut ViewContext<Self>) {
// self.has_focus = false;
// }
// }
// impl Modal for GoToLine {
// fn has_focus(&self) -> bool {
// self.has_focus
// }
// fn dismiss_on_event(event: &Self::Event) -> bool {
// matches!(event, Event::Dismissed)
// }
// }
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
modal(cx)
.id("go to line")
.on_action(Self::cancel)
.on_action(Self::confirm)
.w_96()
.child(
v_stack()
.px_1()
.pt_0p5()
.gap_px()
.child(
v_stack()
.py_0p5()
.px_1()
.child(div().px_1().py_0p5().child(self.line_editor.clone())),
)
.child(
div()
.h_px()
.w_full()
.bg(cx.theme().colors().element_background),
)
.child(
h_stack()
.justify_between()
.px_2()
.py_1()
.child(Label::new(self.current_text.clone()).color(LabelColor::Muted)),
),
)
}
}

View file

@ -123,6 +123,7 @@ pub fn register_action<A: Action>() {
/// Construct an action based on its name and optional JSON parameters sourced from the keymap.
pub fn build_action(name: &str, params: Option<serde_json::Value>) -> Result<Box<dyn Action>> {
let lock = ACTION_REGISTRY.read();
let build_action = lock
.builders_by_name
.get(name)

View file

@ -18,8 +18,8 @@ use crate::{
AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId,
Entity, EventEmitter, FocusEvent, FocusHandle, FocusId, ForegroundExecutor, KeyBinding, Keymap,
LayoutId, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render, SubscriberSet,
Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window,
WindowContext, WindowHandle, WindowId,
Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, ViewContext,
Window, WindowContext, WindowHandle, WindowId,
};
use anyhow::{anyhow, Result};
use collections::{HashMap, HashSet, VecDeque};
@ -167,6 +167,7 @@ type Handler = Box<dyn FnMut(&mut AppContext) -> bool + 'static>;
type Listener = Box<dyn FnMut(&dyn Any, &mut AppContext) -> bool + 'static>;
type QuitHandler = Box<dyn FnOnce(&mut AppContext) -> LocalBoxFuture<'static, ()> + 'static>;
type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut AppContext) + 'static>;
type NewViewListener = Box<dyn FnMut(AnyView, &mut WindowContext) + 'static>;
// struct FrameConsumer {
// next_frame_callbacks: Vec<FrameCallback>,
@ -193,6 +194,7 @@ pub struct AppContext {
pub(crate) text_style_stack: Vec<TextStyleRefinement>,
pub(crate) globals_by_type: HashMap<TypeId, AnyBox>,
pub(crate) entities: EntityMap,
pub(crate) new_view_observers: SubscriberSet<TypeId, NewViewListener>,
pub(crate) windows: SlotMap<WindowId, Option<Window>>,
pub(crate) keymap: Arc<Mutex<Keymap>>,
pub(crate) global_action_listeners:
@ -251,6 +253,7 @@ impl AppContext {
text_style_stack: Vec::new(),
globals_by_type: HashMap::default(),
entities,
new_view_observers: SubscriberSet::new(),
windows: SlotMap::with_key(),
keymap: Arc::new(Mutex::new(Keymap::default())),
global_action_listeners: HashMap::default(),
@ -599,6 +602,7 @@ impl AppContext {
fn apply_notify_effect(&mut self, emitter: EntityId) {
self.pending_notifications.remove(&emitter);
self.observers
.clone()
.retain(&emitter, |handler| handler(self));
@ -838,6 +842,23 @@ impl AppContext {
self.globals_by_type.insert(global_type, lease.global);
}
pub fn observe_new_views<V: 'static>(
&mut self,
on_new: impl 'static + Fn(&mut V, &mut ViewContext<V>),
) -> Subscription {
self.new_view_observers.insert(
TypeId::of::<V>(),
Box::new(move |any_view: AnyView, cx: &mut WindowContext| {
any_view
.downcast::<V>()
.unwrap()
.update(cx, |view_state, cx| {
on_new(view_state, cx);
})
}),
)
}
pub fn observe_release<E, T>(
&mut self,
handle: &E,

View file

@ -258,7 +258,7 @@ impl VisualContext for AsyncWindowContext {
build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static,
V: 'static + Render,
{
self.window
.update(self, |_, cx| cx.build_view(build_view_state))

View file

@ -112,7 +112,7 @@ pub trait VisualContext: Context {
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static;
V: 'static + Render;
fn update_view<V: 'static, R>(
&mut self,

View file

@ -414,10 +414,14 @@ pub trait ElementInteractivity<V: 'static>: 'static {
Box::new(move |_, key_down, context, phase, cx| {
if phase == DispatchPhase::Bubble {
let key_down = key_down.downcast_ref::<KeyDownEvent>().unwrap();
dbg!(key_down);
if let KeyMatch::Some(action) =
cx.match_keystroke(&global_id, &key_down.keystroke, context)
{
dbg!(&action);
return Some(action);
} else {
dbg!("none");
}
}

View file

@ -5,7 +5,7 @@ use crate::{
};
use crate::{BoxShadow, TextStyleRefinement};
use refineable::Refineable;
use smallvec::smallvec;
use smallvec::{smallvec, SmallVec};
pub trait Styled {
fn style(&mut self) -> &mut StyleRefinement;
@ -295,24 +295,11 @@ pub trait Styled {
/// Sets the box shadow of the element.
/// [Docs](https://tailwindcss.com/docs/box-shadow)
fn shadow(mut self) -> Self
fn shadow(mut self, shadows: SmallVec<[BoxShadow; 2]>) -> Self
where
Self: Sized,
{
self.style().box_shadow = Some(smallvec![
BoxShadow {
color: hsla(0., 0., 0., 0.1),
offset: point(px(0.), px(1.)),
blur_radius: px(3.),
spread_radius: px(0.),
},
BoxShadow {
color: hsla(0., 0., 0., 0.1),
offset: point(px(0.), px(1.)),
blur_radius: px(2.),
spread_radius: px(-1.),
}
]);
self.style().box_shadow = Some(shadows);
self
}

View file

@ -1437,7 +1437,7 @@ impl VisualContext for WindowContext<'_> {
build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
V: 'static,
V: 'static + Render,
{
let slot = self.app.entities.reserve();
let view = View {
@ -1445,7 +1445,16 @@ impl VisualContext for WindowContext<'_> {
};
let mut cx = ViewContext::new(&mut *self.app, &mut *self.window, &view);
let entity = build_view_state(&mut cx);
self.entities.insert(slot, entity);
cx.entities.insert(slot, entity);
cx.new_view_observers
.clone()
.retain(&TypeId::of::<V>(), |observer| {
let any_view = AnyView::from(view.clone());
(observer)(any_view, self);
true
});
view
}
@ -2219,7 +2228,7 @@ impl<V> Context for ViewContext<'_, V> {
}
impl<V: 'static> VisualContext for ViewContext<'_, V> {
fn build_view<W: 'static>(
fn build_view<W: Render + 'static>(
&mut self,
build_view_state: impl FnOnce(&mut ViewContext<'_, W>) -> W,
) -> Self::Result<View<W>> {

View file

@ -1,5 +1,10 @@
use gpui::actions;
// todo!(remove this)
// https://github.com/rust-lang/rust/issues/47384
// https://github.com/mmastrac/rust-ctor/issues/280
pub fn unused() {}
actions!(
Cancel,
Confirm,

View file

@ -71,7 +71,7 @@ impl Theme {
&self.styles.system
}
/// Returns the [`ThemeColors`] for the theme.
/// Returns the [`PlayerColors`] for the theme.
#[inline(always)]
pub fn players(&self) -> &PlayerColors {
&self.styles.player

View file

@ -3,6 +3,7 @@ mod button;
mod checkbox;
mod context_menu;
mod details;
mod elevated_surface;
mod facepile;
mod icon;
mod icon_button;
@ -30,6 +31,7 @@ pub use button::*;
pub use checkbox::*;
pub use context_menu::*;
pub use details::*;
pub use elevated_surface::*;
pub use facepile::*;
pub use icon::*;
pub use icon_button::*;

View file

@ -0,0 +1,28 @@
use gpui::Div;
use crate::{prelude::*, v_stack};
/// Create an elevated surface.
///
/// Must be used inside of a relative parent element
pub fn elevated_surface<V: 'static>(level: ElevationIndex, cx: &mut ViewContext<V>) -> Div<V> {
let colors = cx.theme().colors();
// let shadow = BoxShadow {
// color: hsla(0., 0., 0., 0.1),
// offset: point(px(0.), px(1.)),
// blur_radius: px(3.),
// spread_radius: px(0.),
// };
v_stack()
.rounded_lg()
.bg(colors.elevated_surface_background)
.border()
.border_color(colors.border)
.shadow(level.shadow())
}
pub fn modal<V>(cx: &mut ViewContext<V>) -> Div<V> {
elevated_surface(ElevationIndex::ModalSurfaces, cx)
}

View file

@ -1,3 +1,6 @@
use gpui::{hsla, point, px, BoxShadow};
use smallvec::{smallvec, SmallVec};
#[doc = include_str!("elevation.md")]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Elevation {
@ -17,8 +20,8 @@ pub enum ElevationIndex {
}
impl ElevationIndex {
pub fn usize(&self) -> usize {
match *self {
pub fn z_index(self) -> u32 {
match self {
ElevationIndex::AppBackground => 0,
ElevationIndex::UISurface => 100,
ElevationIndex::ElevatedSurface => 200,
@ -27,6 +30,26 @@ impl ElevationIndex {
ElevationIndex::DraggedElement => 900,
}
}
pub fn shadow(self) -> SmallVec<[BoxShadow; 2]> {
match self {
ElevationIndex::AppBackground => smallvec![],
ElevationIndex::UISurface => smallvec![BoxShadow {
color: hsla(0., 0., 0., 0.12),
offset: point(px(0.), px(1.)),
blur_radius: px(3.),
spread_radius: px(0.),
}],
_ => smallvec![BoxShadow {
color: hsla(0., 0., 0., 0.32),
offset: point(px(1.), px(3.)),
blur_radius: px(12.),
spread_radius: px(0.),
}],
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]

View file

@ -407,6 +407,14 @@ impl Dock {
// }
}
impl Render for Dock {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
todo!()
}
}
// todo!()
// impl View for Dock {
// fn ui_name() -> &'static str {

View file

@ -1,35 +1,38 @@
use crate::Workspace;
use gpui::{
div, AnyView, AppContext, Div, ParentElement, Render, StatelessInteractive, View, ViewContext,
div, px, AnyView, Component, Div, EventEmitter, ParentElement, Render, StatelessInteractive,
Styled, Subscription, View, ViewContext,
};
use std::{any::TypeId, sync::Arc};
use ui::v_stack;
pub struct ModalRegistry {
pub struct ModalLayer {
open_modal: Option<AnyView>,
subscription: Option<Subscription>,
registered_modals: Vec<(TypeId, Box<dyn Fn(Div<Workspace>) -> Div<Workspace>>)>,
}
pub trait Modal {}
#[derive(Clone)]
pub struct ModalLayer {
open_modal: Option<AnyView>,
pub enum ModalEvent {
Dismissed,
}
pub fn init_modal_registry(cx: &mut AppContext) {
cx.set_global(ModalRegistry {
registered_modals: Vec::new(),
});
pub trait Modal: EventEmitter + Render {
fn to_modal_event(&self, _: &Self::Event) -> Option<ModalEvent>;
}
struct ToggleModal {
name: String,
}
impl ModalLayer {
pub fn new() -> Self {
Self {
open_modal: None,
subscription: None,
registered_modals: Vec::new(),
}
}
impl ModalRegistry {
pub fn register_modal<A: 'static, V, B>(&mut self, action: A, build_view: B)
where
V: Render,
B: Fn(&Workspace, &mut ViewContext<Workspace>) -> View<V> + 'static,
V: Modal,
B: Fn(&mut Workspace, &mut ViewContext<Workspace>) -> Option<View<V>> + 'static,
{
let build_view = Arc::new(build_view);
@ -38,42 +41,56 @@ impl ModalRegistry {
Box::new(move |mut div| {
let build_view = build_view.clone();
div.on_action(
move |workspace: &mut Workspace, event: &A, cx: &mut ViewContext<Workspace>| {
let new_modal = (build_view)(workspace, cx);
workspace.modal_layer.update(cx, |modal_layer, _| {
modal_layer.open_modal = Some(new_modal.into());
});
cx.notify();
},
)
div.on_action(move |workspace, event: &A, cx| {
let Some(new_modal) = (build_view)(workspace, cx) else {
return;
};
workspace.modal_layer().show_modal(new_modal, cx);
})
}),
));
}
}
impl ModalLayer {
pub fn new() -> Self {
Self { open_modal: None }
pub fn show_modal<V: Modal>(&mut self, new_modal: View<V>, cx: &mut ViewContext<Workspace>) {
self.subscription = Some(cx.subscribe(&new_modal, |this, modal, e, cx| {
match modal.read(cx).to_modal_event(e) {
Some(ModalEvent::Dismissed) => this.modal_layer().hide_modal(cx),
None => {}
}
}));
self.open_modal = Some(new_modal.into());
cx.notify();
}
pub fn render(&self, workspace: &Workspace, cx: &ViewContext<Workspace>) -> Div<Workspace> {
let mut div = div();
pub fn hide_modal(&mut self, cx: &mut ViewContext<Workspace>) {
self.open_modal.take();
self.subscription.take();
cx.notify();
}
// div, c workspace.toggle_modal()div.on_action()) {
//
// }
pub fn wrapper_element(&self, cx: &ViewContext<Workspace>) -> Div<Workspace> {
let mut parent = div().relative().size_full();
// for (type_id, action) in cx.global::<ModalRegistry>().registered_modals.iter() {
// div = div.useful_on_action(*type_id, action)
// }
for (_, action) in cx.global::<ModalRegistry>().registered_modals.iter() {
div = (action)(div);
for (_, action) in self.registered_modals.iter() {
parent = (action)(parent);
}
div.children(self.open_modal.clone())
parent.when_some(self.open_modal.as_ref(), |parent, open_modal| {
let container1 = div()
.absolute()
.flex()
.flex_col()
.items_center()
.size_full()
.top_0()
.left_0()
.z_index(400);
// transparent layer
let container2 = v_stack().h(px(0.0)).relative().top_20();
parent.child(container1.child(container2.child(open_modal.clone())))
})
}
}

View file

@ -1,6 +1,7 @@
use crate::ItemHandle;
use gpui::{
AnyView, AppContext, Entity, EntityId, EventEmitter, Render, View, ViewContext, WindowContext,
AnyView, AppContext, Div, Entity, EntityId, EventEmitter, Render, View, ViewContext,
WindowContext,
};
pub trait ToolbarItemView: Render + EventEmitter {
@ -56,6 +57,14 @@ pub struct Toolbar {
items: Vec<(Box<dyn ToolbarItemViewHandle>, ToolbarItemLocation)>,
}
impl Render for Toolbar {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
todo!()
}
}
// todo!()
// impl View for Toolbar {
// fn ui_name() -> &'static str {

View file

@ -46,8 +46,7 @@ use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings,
use itertools::Itertools;
use language2::LanguageRegistry;
use lazy_static::lazy_static;
pub use modal_layer::ModalRegistry;
use modal_layer::{init_modal_registry, ModalLayer};
pub use modal_layer::*;
use node_runtime::NodeRuntime;
use notifications::{simple_message_notification::MessageNotification, NotificationHandle};
pub use pane::*;
@ -227,7 +226,6 @@ pub fn init_settings(cx: &mut AppContext) {
pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
init_settings(cx);
init_modal_registry(cx);
pane::init(cx);
notifications::init(cx);
@ -547,7 +545,7 @@ pub struct Workspace {
last_active_center_pane: Option<WeakView<Pane>>,
last_active_view_id: Option<proto::ViewId>,
status_bar: View<StatusBar>,
modal_layer: View<ModalLayer>,
modal_layer: ModalLayer,
// titlebar_item: Option<AnyViewHandle>,
notifications: Vec<(TypeId, usize, Box<dyn NotificationHandle>)>,
project: Model<Project>,
@ -698,7 +696,8 @@ impl Workspace {
status_bar
});
let modal_layer = cx.build_view(|cx| ModalLayer::new());
let workspace_handle = cx.view().downgrade();
let modal_layer = ModalLayer::new();
// todo!()
// cx.update_default_global::<DragAndDrop<Workspace>, _, _>(|drag_and_drop, _| {
@ -782,6 +781,10 @@ impl Workspace {
}
}
pub fn modal_layer(&mut self) -> &mut ModalLayer {
&mut self.modal_layer
}
fn new_local(
abs_paths: Vec<PathBuf>,
app_state: Arc<AppState>,
@ -3712,13 +3715,13 @@ impl Render for Workspace {
.bg(cx.theme().colors().background)
.child(self.render_titlebar(cx))
.child(
// todo! should this be a component a view?
self.modal_layer
.read(cx)
.render(self, cx)
.wrapper_element(cx)
.relative()
.flex_1()
.w_full()
.flex()
.flex_row()
.overflow_hidden()
.border_t()
.border_b()

View file

@ -48,6 +48,7 @@ journal = { package = "journal2", path = "../journal2" }
language = { package = "language2", path = "../language2" }
# language_selector = { path = "../language_selector" }
lsp = { package = "lsp2", path = "../lsp2" }
menu = { package = "menu2", path = "../menu2" }
language_tools = { path = "../language_tools" }
node_runtime = { path = "../node_runtime" }
# assistant = { path = "../assistant" }

View file

@ -56,6 +56,10 @@ use zed2::{
mod open_listener;
fn main() {
//TODO!(figure out what the linker issues are here)
// https://github.com/rust-lang/rust/issues/47384
// https://github.com/mmastrac/rust-ctor/issues/280
menu::unused();
let http = http::client();
init_paths();
init_logger();

View file

@ -23,7 +23,7 @@ export default function assistant(): any {
const theme = useTheme()
const interactive_role = (
color: StyleSets
color: StyleSets,
): Interactive<RoleCycleButton> => {
return interactive({
base: {
@ -94,7 +94,7 @@ export default function assistant(): any {
margin: { left: 8, right: 18 },
color: foreground(theme.highest, "positive"),
width: 12,
}
},
},
retrieve_context: toggleable({
base: interactive({
@ -106,7 +106,8 @@ export default function assistant(): any {
background: background(theme.highest, "on"),
corner_radius: 2,
border: {
width: 1., color: background(theme.highest, "on")
width: 1,
color: background(theme.highest, "on"),
},
margin: { left: 2 },
padding: {
@ -118,17 +119,45 @@ export default function assistant(): any {
},
state: {
hovered: {
...text(theme.highest, "mono", "variant", "hovered"),
background: background(theme.highest, "on", "hovered"),
...text(
theme.highest,
"mono",
"variant",
"hovered",
),
background: background(
theme.highest,
"on",
"hovered",
),
border: {
width: 1., color: background(theme.highest, "on", "hovered")
width: 1,
color: background(
theme.highest,
"on",
"hovered",
),
},
},
clicked: {
...text(theme.highest, "mono", "variant", "pressed"),
background: background(theme.highest, "on", "pressed"),
...text(
theme.highest,
"mono",
"variant",
"pressed",
),
background: background(
theme.highest,
"on",
"pressed",
),
border: {
width: 1., color: background(theme.highest, "on", "pressed")
width: 1,
color: background(
theme.highest,
"on",
"pressed",
),
},
},
},
@ -143,11 +172,19 @@ export default function assistant(): any {
border: border(theme.highest, "accent"),
},
hovered: {
background: background(theme.highest, "accent", "hovered"),
background: background(
theme.highest,
"accent",
"hovered",
),
border: border(theme.highest, "accent", "hovered"),
},
clicked: {
background: background(theme.highest, "accent", "pressed"),
background: background(
theme.highest,
"accent",
"pressed",
),
border: border(theme.highest, "accent", "pressed"),
},
},
@ -163,7 +200,8 @@ export default function assistant(): any {
background: background(theme.highest, "on"),
corner_radius: 2,
border: {
width: 1., color: background(theme.highest, "on")
width: 1,
color: background(theme.highest, "on"),
},
padding: {
left: 4,
@ -174,17 +212,45 @@ export default function assistant(): any {
},
state: {
hovered: {
...text(theme.highest, "mono", "variant", "hovered"),
background: background(theme.highest, "on", "hovered"),
...text(
theme.highest,
"mono",
"variant",
"hovered",
),
background: background(
theme.highest,
"on",
"hovered",
),
border: {
width: 1., color: background(theme.highest, "on", "hovered")
width: 1,
color: background(
theme.highest,
"on",
"hovered",
),
},
},
clicked: {
...text(theme.highest, "mono", "variant", "pressed"),
background: background(theme.highest, "on", "pressed"),
...text(
theme.highest,
"mono",
"variant",
"pressed",
),
background: background(
theme.highest,
"on",
"pressed",
),
border: {
width: 1., color: background(theme.highest, "on", "pressed")
width: 1,
color: background(
theme.highest,
"on",
"pressed",
),
},
},
},
@ -199,11 +265,19 @@ export default function assistant(): any {
border: border(theme.highest, "accent"),
},
hovered: {
background: background(theme.highest, "accent", "hovered"),
background: background(
theme.highest,
"accent",
"hovered",
),
border: border(theme.highest, "accent", "hovered"),
},
clicked: {
background: background(theme.highest, "accent", "pressed"),
background: background(
theme.highest,
"accent",
"pressed",
),
border: border(theme.highest, "accent", "pressed"),
},
},

View file

@ -78,33 +78,33 @@ export default function status_bar(): any {
padding: { top: 2, bottom: 2, left: 6, right: 6 },
},
container_warning: diagnostic_status_container,
container_error: diagnostic_status_container
container_error: diagnostic_status_container,
},
state: {
hovered: {
icon_color_ok: foreground(layer, "on"),
container_ok: {
background: background(layer, "hovered")
background: background(layer, "hovered"),
},
container_warning: {
background: background(layer, "hovered")
background: background(layer, "hovered"),
},
container_error: {
background: background(layer, "hovered")
background: background(layer, "hovered"),
},
},
clicked: {
icon_color_ok: foreground(layer, "on"),
container_ok: {
background: background(layer, "pressed")
background: background(layer, "pressed"),
},
container_warning: {
background: background(layer, "pressed")
background: background(layer, "pressed"),
},
container_error: {
background: background(layer, "pressed")
}
}
background: background(layer, "pressed"),
},
},
},
}),
panel_buttons: {

View file

@ -31,7 +31,7 @@ export const theme: ThemeConfig = {
color.muted,
color.subtle,
color.text,
].reverse()
].reverse(),
)
.domain([0, 0.35, 0.45, 0.65, 0.7, 0.8, 0.9, 1]),
red: color_ramp(chroma(color.love)),