Merge pull request #1485 from zed-industries/fullscreen-workspace-title-padding
Fullscreen workspace title padding
This commit is contained in:
commit
c303c4e8f9
7 changed files with 445 additions and 397 deletions
File diff suppressed because it is too large
Load diff
106
crates/gpui/src/app/callback_collection.rs
Normal file
106
crates/gpui/src/app/callback_collection.rs
Normal file
|
@ -0,0 +1,106 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::{hash::Hash, sync::Weak};
|
||||||
|
|
||||||
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
use collections::{btree_map, BTreeMap, HashMap};
|
||||||
|
|
||||||
|
use crate::MutableAppContext;
|
||||||
|
|
||||||
|
pub type Mapping<K, F> = Mutex<HashMap<K, BTreeMap<usize, Option<F>>>>;
|
||||||
|
|
||||||
|
pub struct CallbackCollection<K: Hash + Eq, F> {
|
||||||
|
internal: Arc<Mapping<K, F>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Hash + Eq, F> Clone for CallbackCollection<K, F> {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
internal: self.internal.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Hash + Eq + Copy, F> Default for CallbackCollection<K, F> {
|
||||||
|
fn default() -> Self {
|
||||||
|
CallbackCollection {
|
||||||
|
internal: Arc::new(Mutex::new(Default::default())),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<K: Hash + Eq + Copy, F> CallbackCollection<K, F> {
|
||||||
|
pub fn downgrade(&self) -> Weak<Mapping<K, F>> {
|
||||||
|
Arc::downgrade(&self.internal)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.internal.lock().is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_callback(&mut self, id: K, subscription_id: usize, callback: F) {
|
||||||
|
self.internal
|
||||||
|
.lock()
|
||||||
|
.entry(id)
|
||||||
|
.or_default()
|
||||||
|
.insert(subscription_id, Some(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn remove(&mut self, id: K) {
|
||||||
|
self.internal.lock().remove(&id);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_or_remove_callback(&mut self, id: K, subscription_id: usize, callback: F) {
|
||||||
|
match self
|
||||||
|
.internal
|
||||||
|
.lock()
|
||||||
|
.entry(id)
|
||||||
|
.or_default()
|
||||||
|
.entry(subscription_id)
|
||||||
|
{
|
||||||
|
btree_map::Entry::Vacant(entry) => {
|
||||||
|
entry.insert(Some(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
btree_map::Entry::Occupied(entry) => {
|
||||||
|
// TODO: This seems like it should never be called because no code
|
||||||
|
// should ever attempt to remove an existing callback
|
||||||
|
debug_assert!(entry.get().is_none());
|
||||||
|
entry.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn emit_and_cleanup<C: FnMut(&mut F, &mut MutableAppContext) -> bool>(
|
||||||
|
&mut self,
|
||||||
|
id: K,
|
||||||
|
cx: &mut MutableAppContext,
|
||||||
|
mut call_callback: C,
|
||||||
|
) {
|
||||||
|
let callbacks = self.internal.lock().remove(&id);
|
||||||
|
if let Some(callbacks) = callbacks {
|
||||||
|
for (subscription_id, callback) in callbacks {
|
||||||
|
if let Some(mut callback) = callback {
|
||||||
|
let alive = call_callback(&mut callback, cx);
|
||||||
|
if alive {
|
||||||
|
match self
|
||||||
|
.internal
|
||||||
|
.lock()
|
||||||
|
.entry(id)
|
||||||
|
.or_default()
|
||||||
|
.entry(subscription_id)
|
||||||
|
{
|
||||||
|
btree_map::Entry::Vacant(entry) => {
|
||||||
|
entry.insert(Some(callback));
|
||||||
|
}
|
||||||
|
btree_map::Entry::Occupied(entry) => {
|
||||||
|
entry.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -112,6 +112,7 @@ pub trait Window: WindowContext {
|
||||||
fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>);
|
fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>);
|
||||||
fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>);
|
fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>);
|
||||||
fn on_resize(&mut self, callback: Box<dyn FnMut()>);
|
fn on_resize(&mut self, callback: Box<dyn FnMut()>);
|
||||||
|
fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>);
|
||||||
fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>);
|
fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>);
|
||||||
fn on_close(&mut self, callback: Box<dyn FnOnce()>);
|
fn on_close(&mut self, callback: Box<dyn FnOnce()>);
|
||||||
fn set_input_handler(&mut self, input_handler: Box<dyn InputHandler>);
|
fn set_input_handler(&mut self, input_handler: Box<dyn InputHandler>);
|
||||||
|
|
|
@ -128,6 +128,14 @@ unsafe fn build_classes() {
|
||||||
sel!(windowDidResize:),
|
sel!(windowDidResize:),
|
||||||
window_did_resize as extern "C" fn(&Object, Sel, id),
|
window_did_resize as extern "C" fn(&Object, Sel, id),
|
||||||
);
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(windowWillEnterFullScreen:),
|
||||||
|
window_will_enter_fullscreen as extern "C" fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
|
decl.add_method(
|
||||||
|
sel!(windowWillExitFullScreen:),
|
||||||
|
window_will_exit_fullscreen as extern "C" fn(&Object, Sel, id),
|
||||||
|
);
|
||||||
decl.add_method(
|
decl.add_method(
|
||||||
sel!(windowDidBecomeKey:),
|
sel!(windowDidBecomeKey:),
|
||||||
window_did_change_key_status as extern "C" fn(&Object, Sel, id),
|
window_did_change_key_status as extern "C" fn(&Object, Sel, id),
|
||||||
|
@ -276,6 +284,7 @@ struct WindowState {
|
||||||
event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
|
event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
|
||||||
activate_callback: Option<Box<dyn FnMut(bool)>>,
|
activate_callback: Option<Box<dyn FnMut(bool)>>,
|
||||||
resize_callback: Option<Box<dyn FnMut()>>,
|
resize_callback: Option<Box<dyn FnMut()>>,
|
||||||
|
fullscreen_callback: Option<Box<dyn FnMut(bool)>>,
|
||||||
should_close_callback: Option<Box<dyn FnMut() -> bool>>,
|
should_close_callback: Option<Box<dyn FnMut() -> bool>>,
|
||||||
close_callback: Option<Box<dyn FnOnce()>>,
|
close_callback: Option<Box<dyn FnOnce()>>,
|
||||||
input_handler: Option<Box<dyn InputHandler>>,
|
input_handler: Option<Box<dyn InputHandler>>,
|
||||||
|
@ -368,6 +377,7 @@ impl Window {
|
||||||
should_close_callback: None,
|
should_close_callback: None,
|
||||||
close_callback: None,
|
close_callback: None,
|
||||||
activate_callback: None,
|
activate_callback: None,
|
||||||
|
fullscreen_callback: None,
|
||||||
input_handler: None,
|
input_handler: None,
|
||||||
pending_key_down: None,
|
pending_key_down: None,
|
||||||
performed_key_equivalent: false,
|
performed_key_equivalent: false,
|
||||||
|
@ -467,6 +477,10 @@ impl platform::Window for Window {
|
||||||
self.0.as_ref().borrow_mut().resize_callback = Some(callback);
|
self.0.as_ref().borrow_mut().resize_callback = Some(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>) {
|
||||||
|
self.0.as_ref().borrow_mut().fullscreen_callback = Some(callback);
|
||||||
|
}
|
||||||
|
|
||||||
fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
|
fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
|
||||||
self.0.as_ref().borrow_mut().should_close_callback = Some(callback);
|
self.0.as_ref().borrow_mut().should_close_callback = Some(callback);
|
||||||
}
|
}
|
||||||
|
@ -908,6 +922,24 @@ extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
|
||||||
window_state.as_ref().borrow().move_traffic_light();
|
window_state.as_ref().borrow().move_traffic_light();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
|
||||||
|
window_fullscreen_changed(this, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) {
|
||||||
|
window_fullscreen_changed(this, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn window_fullscreen_changed(this: &Object, is_fullscreen: bool) {
|
||||||
|
let window_state = unsafe { get_window_state(this) };
|
||||||
|
let mut window_state_borrow = window_state.as_ref().borrow_mut();
|
||||||
|
if let Some(mut callback) = window_state_borrow.fullscreen_callback.take() {
|
||||||
|
drop(window_state_borrow);
|
||||||
|
callback(is_fullscreen);
|
||||||
|
window_state.borrow_mut().fullscreen_callback = Some(callback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) {
|
extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) {
|
||||||
let is_active = if selector == sel!(windowDidBecomeKey:) {
|
let is_active = if selector == sel!(windowDidBecomeKey:) {
|
||||||
true
|
true
|
||||||
|
|
|
@ -37,6 +37,7 @@ pub struct Window {
|
||||||
event_handlers: Vec<Box<dyn FnMut(super::Event) -> bool>>,
|
event_handlers: Vec<Box<dyn FnMut(super::Event) -> bool>>,
|
||||||
resize_handlers: Vec<Box<dyn FnMut()>>,
|
resize_handlers: Vec<Box<dyn FnMut()>>,
|
||||||
close_handlers: Vec<Box<dyn FnOnce()>>,
|
close_handlers: Vec<Box<dyn FnOnce()>>,
|
||||||
|
fullscreen_handlers: Vec<Box<dyn FnMut(bool)>>,
|
||||||
pub(crate) active_status_change_handlers: Vec<Box<dyn FnMut(bool)>>,
|
pub(crate) active_status_change_handlers: Vec<Box<dyn FnMut(bool)>>,
|
||||||
pub(crate) should_close_handler: Option<Box<dyn FnMut() -> bool>>,
|
pub(crate) should_close_handler: Option<Box<dyn FnMut() -> bool>>,
|
||||||
pub(crate) title: Option<String>,
|
pub(crate) title: Option<String>,
|
||||||
|
@ -199,6 +200,7 @@ impl Window {
|
||||||
close_handlers: Default::default(),
|
close_handlers: Default::default(),
|
||||||
should_close_handler: Default::default(),
|
should_close_handler: Default::default(),
|
||||||
active_status_change_handlers: Default::default(),
|
active_status_change_handlers: Default::default(),
|
||||||
|
fullscreen_handlers: Default::default(),
|
||||||
scale_factor: 1.0,
|
scale_factor: 1.0,
|
||||||
current_scene: None,
|
current_scene: None,
|
||||||
title: None,
|
title: None,
|
||||||
|
@ -253,6 +255,10 @@ impl super::Window for Window {
|
||||||
self.active_status_change_handlers.push(callback);
|
self.active_status_change_handlers.push(callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>) {
|
||||||
|
self.fullscreen_handlers.push(callback)
|
||||||
|
}
|
||||||
|
|
||||||
fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
|
fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
|
||||||
self.resize_handlers.push(callback);
|
self.resize_handlers.push(callback);
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,11 +13,11 @@ use crate::{
|
||||||
ReadModel, ReadView, RenderContext, RenderParams, Scene, UpgradeModelHandle, UpgradeViewHandle,
|
ReadModel, ReadView, RenderContext, RenderParams, Scene, UpgradeModelHandle, UpgradeViewHandle,
|
||||||
View, ViewHandle, WeakModelHandle, WeakViewHandle,
|
View, ViewHandle, WeakModelHandle, WeakViewHandle,
|
||||||
};
|
};
|
||||||
|
use collections::{HashMap, HashSet};
|
||||||
use pathfinder_geometry::vector::{vec2f, Vector2F};
|
use pathfinder_geometry::vector::{vec2f, Vector2F};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
|
||||||
marker::PhantomData,
|
marker::PhantomData,
|
||||||
ops::{Deref, DerefMut, Range},
|
ops::{Deref, DerefMut, Range},
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
|
@ -52,7 +52,7 @@ impl Presenter {
|
||||||
Self {
|
Self {
|
||||||
window_id,
|
window_id,
|
||||||
rendered_views: cx.render_views(window_id, titlebar_height),
|
rendered_views: cx.render_views(window_id, titlebar_height),
|
||||||
parents: HashMap::new(),
|
parents: Default::default(),
|
||||||
cursor_regions: Default::default(),
|
cursor_regions: Default::default(),
|
||||||
mouse_regions: Default::default(),
|
mouse_regions: Default::default(),
|
||||||
font_cache,
|
font_cache,
|
||||||
|
|
|
@ -823,6 +823,8 @@ enum FollowerItem {
|
||||||
|
|
||||||
impl Workspace {
|
impl Workspace {
|
||||||
pub fn new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
|
pub fn new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
|
||||||
|
cx.observe_fullscreen(|_, _, cx| cx.notify()).detach();
|
||||||
|
|
||||||
cx.observe_window_activation(Self::on_window_activation_changed)
|
cx.observe_window_activation(Self::on_window_activation_changed)
|
||||||
.detach();
|
.detach();
|
||||||
cx.observe(&project, |_, _, cx| cx.notify()).detach();
|
cx.observe(&project, |_, _, cx| cx.notify()).detach();
|
||||||
|
@ -1856,6 +1858,17 @@ impl Workspace {
|
||||||
worktree_root_names.push_str(name);
|
worktree_root_names.push_str(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: There should be a better system in place for this
|
||||||
|
// (https://github.com/zed-industries/zed/issues/1290)
|
||||||
|
let is_fullscreen = cx.window_is_fullscreen(cx.window_id());
|
||||||
|
let container_theme = if is_fullscreen {
|
||||||
|
let mut container_theme = theme.workspace.titlebar.container;
|
||||||
|
container_theme.padding.left = container_theme.padding.right;
|
||||||
|
container_theme
|
||||||
|
} else {
|
||||||
|
theme.workspace.titlebar.container
|
||||||
|
};
|
||||||
|
|
||||||
ConstrainedBox::new(
|
ConstrainedBox::new(
|
||||||
Container::new(
|
Container::new(
|
||||||
Stack::new()
|
Stack::new()
|
||||||
|
@ -1883,7 +1896,7 @@ impl Workspace {
|
||||||
)
|
)
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_style(theme.workspace.titlebar.container)
|
.with_style(container_theme)
|
||||||
.boxed(),
|
.boxed(),
|
||||||
)
|
)
|
||||||
.with_height(theme.workspace.titlebar.height)
|
.with_height(theme.workspace.titlebar.height)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue