Add double click handling
This commit is contained in:
parent
3198eb6c6d
commit
f03c0f6e63
6 changed files with 115 additions and 10 deletions
|
@ -514,6 +514,10 @@ impl AppContext {
|
||||||
self.platform.path_for_auxiliary_executable(name)
|
self.platform.path_for_auxiliary_executable(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn double_click_interval(&self) -> Duration {
|
||||||
|
self.platform.double_click_interval()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn prompt_for_paths(
|
pub fn prompt_for_paths(
|
||||||
&self,
|
&self,
|
||||||
options: PathPromptOptions,
|
options: PathPromptOptions,
|
||||||
|
|
|
@ -102,6 +102,7 @@ pub(crate) trait Platform: 'static {
|
||||||
fn app_version(&self) -> Result<SemanticVersion>;
|
fn app_version(&self) -> Result<SemanticVersion>;
|
||||||
fn app_path(&self) -> Result<PathBuf>;
|
fn app_path(&self) -> Result<PathBuf>;
|
||||||
fn local_timezone(&self) -> UtcOffset;
|
fn local_timezone(&self) -> UtcOffset;
|
||||||
|
fn double_click_interval(&self) -> Duration;
|
||||||
fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf>;
|
fn path_for_auxiliary_executable(&self, name: &str) -> Result<PathBuf>;
|
||||||
|
|
||||||
fn set_cursor_style(&self, style: CursorStyle);
|
fn set_cursor_style(&self, style: CursorStyle);
|
||||||
|
|
|
@ -48,6 +48,7 @@ use std::{
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
slice, str,
|
slice, str,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
use time::UtcOffset;
|
use time::UtcOffset;
|
||||||
|
|
||||||
|
@ -650,6 +651,13 @@ impl Platform for MacPlatform {
|
||||||
"macOS"
|
"macOS"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn double_click_interval(&self) -> Duration {
|
||||||
|
unsafe {
|
||||||
|
let double_click_interval: f64 = msg_send![class!(NSEvent), doubleClickInterval];
|
||||||
|
Duration::from_secs_f64(double_click_interval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn os_version(&self) -> Result<SemanticVersion> {
|
fn os_version(&self) -> Result<SemanticVersion> {
|
||||||
unsafe {
|
unsafe {
|
||||||
let process_info = NSProcessInfo::processInfo(nil);
|
let process_info = NSProcessInfo::processInfo(nil);
|
||||||
|
|
|
@ -11,6 +11,7 @@ use std::{
|
||||||
path::PathBuf,
|
path::PathBuf,
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct TestPlatform {
|
pub struct TestPlatform {
|
||||||
|
@ -272,4 +273,8 @@ impl Platform for TestPlatform {
|
||||||
fn delete_credentials(&self, _url: &str) -> Result<()> {
|
fn delete_credentials(&self, _url: &str) -> Result<()> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn double_click_interval(&self) -> std::time::Duration {
|
||||||
|
Duration::from_millis(500)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::{status_bar::StatusItemView, Workspace};
|
use crate::{status_bar::StatusItemView, Workspace};
|
||||||
|
use crate::{DockClickReset, DockDragState};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
div, px, Action, AnchorCorner, AnyView, AppContext, Axis, Div, Entity, EntityId, EventEmitter,
|
div, px, Action, AnchorCorner, AnyView, AppContext, Axis, ClickEvent, Div, Entity, EntityId,
|
||||||
FocusHandle, FocusableView, IntoElement, ParentElement, Render, SharedString, Styled,
|
EventEmitter, FocusHandle, FocusableView, IntoElement, MouseButton, ParentElement, Render,
|
||||||
Subscription, View, ViewContext, VisualContext, WeakView, WindowContext,
|
SharedString, Styled, Subscription, View, ViewContext, VisualContext, WeakView, WindowContext,
|
||||||
};
|
};
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
@ -364,7 +365,7 @@ impl Dock {
|
||||||
this.set_open(false, cx);
|
this.set_open(false, cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PanelEvent::Focus => todo!(),
|
PanelEvent::Focus => {}
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -485,6 +486,48 @@ impl Render for Dock {
|
||||||
if let Some(entry) = self.visible_entry() {
|
if let Some(entry) = self.visible_entry() {
|
||||||
let size = entry.panel.size(cx);
|
let size = entry.panel.size(cx);
|
||||||
|
|
||||||
|
let mut pre_resize_handle = None;
|
||||||
|
let mut post_resize_handle = None;
|
||||||
|
let position = self.position;
|
||||||
|
let handler = div()
|
||||||
|
.id("resize-handle")
|
||||||
|
.bg(gpui::red())
|
||||||
|
.on_mouse_down(gpui::MouseButton::Left, move |_, cx| {
|
||||||
|
cx.update_global(|drag: &mut DockDragState, cx| drag.0 = Some(position))
|
||||||
|
})
|
||||||
|
.on_click(cx.listener(|v, e: &ClickEvent, cx| {
|
||||||
|
if e.down.button == MouseButton::Left {
|
||||||
|
cx.update_global(|state: &mut DockClickReset, cx| {
|
||||||
|
if state.0.is_some() {
|
||||||
|
state.0 = None;
|
||||||
|
v.resize_active_panel(None, cx)
|
||||||
|
} else {
|
||||||
|
let double_click = cx.double_click_interval();
|
||||||
|
let timer = cx.background_executor().timer(double_click);
|
||||||
|
state.0 = Some(cx.spawn(|_, mut cx| async move {
|
||||||
|
timer.await;
|
||||||
|
cx.update_global(|state: &mut DockClickReset, cx| {
|
||||||
|
state.0 = None;
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
match self.position() {
|
||||||
|
DockPosition::Left => {
|
||||||
|
post_resize_handle = Some(handler.w_2().h_full().cursor_col_resize())
|
||||||
|
}
|
||||||
|
DockPosition::Bottom => {
|
||||||
|
pre_resize_handle = Some(handler.w_full().h_2().cursor_row_resize())
|
||||||
|
}
|
||||||
|
DockPosition::Right => {
|
||||||
|
pre_resize_handle = Some(handler.w_full().h_1().cursor_col_resize())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
div()
|
div()
|
||||||
.border_color(cx.theme().colors().border)
|
.border_color(cx.theme().colors().border)
|
||||||
.map(|this| match self.position().axis() {
|
.map(|this| match self.position().axis() {
|
||||||
|
@ -496,7 +539,9 @@ impl Render for Dock {
|
||||||
DockPosition::Right => this.border_l(),
|
DockPosition::Right => this.border_l(),
|
||||||
DockPosition::Bottom => this.border_t(),
|
DockPosition::Bottom => this.border_t(),
|
||||||
})
|
})
|
||||||
|
.children(pre_resize_handle)
|
||||||
.child(entry.panel.to_any())
|
.child(entry.panel.to_any())
|
||||||
|
.children(post_resize_handle)
|
||||||
} else {
|
} else {
|
||||||
div()
|
div()
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,12 +29,12 @@ use futures::{
|
||||||
Future, FutureExt, StreamExt,
|
Future, FutureExt, StreamExt,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, div, impl_actions, point, size, Action, AnyModel, AnyView, AnyWeakView,
|
actions, canvas, div, impl_actions, point, size, Action, AnyModel, AnyView, AnyWeakView,
|
||||||
AnyWindowHandle, AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Context, Div, Entity,
|
AnyWindowHandle, AppContext, AsyncAppContext, AsyncWindowContext, Bounds, Context, Div, Entity,
|
||||||
EntityId, EventEmitter, FocusHandle, FocusableView, GlobalPixels, InteractiveElement,
|
EntityId, EventEmitter, FocusHandle, FocusableView, GlobalPixels, InteractiveElement,
|
||||||
KeyContext, ManagedView, Model, ModelContext, ParentElement, PathPromptOptions, Point,
|
KeyContext, ManagedView, Model, ModelContext, MouseMoveEvent, ParentElement, PathPromptOptions,
|
||||||
PromptLevel, Render, Size, Styled, Subscription, Task, View, ViewContext, VisualContext,
|
Pixels, Point, PromptLevel, Render, Size, Styled, Subscription, Task, View, ViewContext,
|
||||||
WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
|
VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle, WindowOptions,
|
||||||
};
|
};
|
||||||
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
|
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
|
@ -227,6 +227,9 @@ pub fn init_settings(cx: &mut AppContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
|
pub fn init(app_state: Arc<AppState>, cx: &mut AppContext) {
|
||||||
|
cx.default_global::<DockDragState>();
|
||||||
|
cx.default_global::<DockClickReset>();
|
||||||
|
|
||||||
init_settings(cx);
|
init_settings(cx);
|
||||||
notifications::init(cx);
|
notifications::init(cx);
|
||||||
|
|
||||||
|
@ -480,8 +483,6 @@ struct FollowerState {
|
||||||
items_by_leader_view_id: HashMap<ViewId, Box<dyn FollowableItemHandle>>,
|
items_by_leader_view_id: HashMap<ViewId, Box<dyn FollowableItemHandle>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
enum WorkspaceBounds {}
|
|
||||||
|
|
||||||
impl Workspace {
|
impl Workspace {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
workspace_id: WorkspaceId,
|
workspace_id: WorkspaceId,
|
||||||
|
@ -3571,6 +3572,16 @@ impl FocusableView for Workspace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct WorkspaceBounds(Bounds<Pixels>);
|
||||||
|
|
||||||
|
//todo!("remove this when better drag APIs are in GPUI2")
|
||||||
|
#[derive(Default)]
|
||||||
|
struct DockDragState(Option<DockPosition>);
|
||||||
|
|
||||||
|
//todo!("remove this when better double APIs are in GPUI2")
|
||||||
|
#[derive(Default)]
|
||||||
|
struct DockClickReset(Option<Task<()>>);
|
||||||
|
|
||||||
impl Render for Workspace {
|
impl Render for Workspace {
|
||||||
type Element = Div;
|
type Element = Div;
|
||||||
|
|
||||||
|
@ -3614,6 +3625,37 @@ impl Render for Workspace {
|
||||||
.border_t()
|
.border_t()
|
||||||
.border_b()
|
.border_b()
|
||||||
.border_color(cx.theme().colors().border)
|
.border_color(cx.theme().colors().border)
|
||||||
|
.on_mouse_up(gpui::MouseButton::Left, |_, cx| {
|
||||||
|
cx.update_global(|drag: &mut DockDragState, cx| {
|
||||||
|
drag.0 = None;
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.on_mouse_move(cx.listener(|workspace, e: &MouseMoveEvent, cx| {
|
||||||
|
if let Some(types) = &cx.global::<DockDragState>().0 {
|
||||||
|
let workspace_bounds = cx.global::<WorkspaceBounds>().0;
|
||||||
|
match types {
|
||||||
|
DockPosition::Left => {
|
||||||
|
let size = e.position.x;
|
||||||
|
workspace.left_dock.update(cx, |left_dock, cx| {
|
||||||
|
left_dock.resize_active_panel(Some(size.0), cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
DockPosition::Right => {
|
||||||
|
let size = workspace_bounds.size.width - e.position.x;
|
||||||
|
workspace.right_dock.update(cx, |right_dock, cx| {
|
||||||
|
right_dock.resize_active_panel(Some(size.0), cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
DockPosition::Bottom => {
|
||||||
|
let size = workspace_bounds.size.height - e.position.y;
|
||||||
|
workspace.bottom_dock.update(cx, |bottom_dock, cx| {
|
||||||
|
bottom_dock.resize_active_panel(Some(size.0), cx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
.child(canvas(|bounds, cx| cx.set_global(WorkspaceBounds(bounds))))
|
||||||
.child(self.modal_layer.clone())
|
.child(self.modal_layer.clone())
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue