logiguard fork: GPUI xdg-activation keyboard-focus serial fix
Some checks failed
Update All Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Triage Project Sync (#84) / Sync triage project (push) Has been cancelled
release_nightly / notify_on_failure (push) Has been cancelled
release_nightly / check_style (push) Has been cancelled
release_nightly / run_tests_windows (push) Has been cancelled
release_nightly / clippy_windows (push) Has been cancelled
release_nightly / bundle_linux_aarch64 (push) Has been cancelled
release_nightly / bundle_linux_x86_64 (push) Has been cancelled
release_nightly / bundle_mac_aarch64 (push) Has been cancelled
release_nightly / bundle_mac_x86_64 (push) Has been cancelled
release_nightly / bundle_windows_aarch64 (push) Has been cancelled
release_nightly / bundle_windows_x86_64 (push) Has been cancelled
release_nightly / build_nix_linux_x86_64 (push) Has been cancelled
release_nightly / build_nix_mac_aarch64 (push) Has been cancelled
release_nightly / update_nightly_tag (push) Has been cancelled
Hotfix Review Monitor / check-hotfix-reviews (push) Has been cancelled
Stale PR Review Reminder / check-stale-prs (push) Has been cancelled
Update Weekly Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Bump collab-staging Tag / update-collab-staging-tag (push) Has been cancelled
compliance_check / scheduled_compliance_check (push) Has been cancelled
Some checks failed
Update All Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Triage Project Sync (#84) / Sync triage project (push) Has been cancelled
release_nightly / notify_on_failure (push) Has been cancelled
release_nightly / check_style (push) Has been cancelled
release_nightly / run_tests_windows (push) Has been cancelled
release_nightly / clippy_windows (push) Has been cancelled
release_nightly / bundle_linux_aarch64 (push) Has been cancelled
release_nightly / bundle_linux_x86_64 (push) Has been cancelled
release_nightly / bundle_mac_aarch64 (push) Has been cancelled
release_nightly / bundle_mac_x86_64 (push) Has been cancelled
release_nightly / bundle_windows_aarch64 (push) Has been cancelled
release_nightly / bundle_windows_x86_64 (push) Has been cancelled
release_nightly / build_nix_linux_x86_64 (push) Has been cancelled
release_nightly / build_nix_mac_aarch64 (push) Has been cancelled
release_nightly / update_nightly_tag (push) Has been cancelled
Hotfix Review Monitor / check-hotfix-reviews (push) Has been cancelled
Stale PR Review Reminder / check-stale-prs (push) Has been cancelled
Update Weekly Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Bump collab-staging Tag / update-collab-staging-tag (push) Has been cancelled
compliance_check / scheduled_compliance_check (push) Has been cancelled
Single-commit orphan branch: full zed-industries/zed @ 8c74db0 source tree with a 3-file patch applied (no upstream history). Patch (crates/gpui_linux/src/linux/wayland/): - serial.rs: add SerialKind::KeyboardEnter - client.rs: store wl_keyboard.enter serial; add latest_serial_of() - window.rs: activate() uses keyboard-enter serial (Mutter focus gate) Mutter honors window activation only when the token carries the keyboard- focus serial from wl_keyboard.enter; GPUI used a stale mouse-press serial. See docs/tray-window-focus-wayland.md in logiguard. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
23
crates/line_ending_selector/Cargo.toml
Normal file
23
crates/line_ending_selector/Cargo.toml
Normal file
@@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "line_ending_selector"
|
||||
version = "0.1.0"
|
||||
edition.workspace = true
|
||||
publish.workspace = true
|
||||
license = "GPL-3.0-or-later"
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
path = "src/line_ending_selector.rs"
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
editor.workspace = true
|
||||
gpui.workspace = true
|
||||
language.workspace = true
|
||||
picker.workspace = true
|
||||
project.workspace = true
|
||||
ui.workspace = true
|
||||
util.workspace = true
|
||||
workspace.workspace = true
|
||||
1
crates/line_ending_selector/LICENSE-GPL
Symbolic link
1
crates/line_ending_selector/LICENSE-GPL
Symbolic link
@@ -0,0 +1 @@
|
||||
../../LICENSE-GPL
|
||||
79
crates/line_ending_selector/src/line_ending_indicator.rs
Normal file
79
crates/line_ending_selector/src/line_ending_indicator.rs
Normal file
@@ -0,0 +1,79 @@
|
||||
use editor::Editor;
|
||||
use gpui::{App, Entity, Subscription, WeakEntity};
|
||||
use language::LineEnding;
|
||||
use ui::{Tooltip, prelude::*};
|
||||
use workspace::{
|
||||
HideStatusItem, StatusBarSettings, StatusItemView, item::ItemHandle, item::Settings,
|
||||
};
|
||||
|
||||
use crate::{LineEndingSelector, Toggle};
|
||||
|
||||
#[derive(Default)]
|
||||
pub struct LineEndingIndicator {
|
||||
line_ending: Option<LineEnding>,
|
||||
active_editor: Option<WeakEntity<Editor>>,
|
||||
_observe_active_editor: Option<Subscription>,
|
||||
}
|
||||
|
||||
impl LineEndingIndicator {
|
||||
fn update(&mut self, editor: Entity<Editor>, _: &mut Window, cx: &mut Context<Self>) {
|
||||
self.line_ending = None;
|
||||
self.active_editor = None;
|
||||
|
||||
if let Some(buffer) = editor.read(cx).active_buffer(cx) {
|
||||
let line_ending = buffer.read(cx).line_ending();
|
||||
self.line_ending = Some(line_ending);
|
||||
self.active_editor = Some(editor.downgrade());
|
||||
}
|
||||
|
||||
cx.notify();
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for LineEndingIndicator {
|
||||
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
if !StatusBarSettings::get_global(cx).line_endings_button {
|
||||
return div();
|
||||
}
|
||||
|
||||
div().when_some(self.line_ending.as_ref(), |el, line_ending| {
|
||||
el.child(
|
||||
Button::new("change-line-ending", line_ending.label())
|
||||
.label_size(LabelSize::Small)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
if let Some(editor) = this.active_editor.as_ref() {
|
||||
LineEndingSelector::toggle(editor, window, cx);
|
||||
}
|
||||
}))
|
||||
.tooltip(|_window, cx| Tooltip::for_action("Select Line Ending", &Toggle, cx)),
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl StatusItemView for LineEndingIndicator {
|
||||
fn set_active_pane_item(
|
||||
&mut self,
|
||||
active_pane_item: Option<&dyn ItemHandle>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
|
||||
self._observe_active_editor = Some(cx.observe_in(&editor, window, Self::update));
|
||||
self.update(editor, window, cx);
|
||||
} else {
|
||||
self.line_ending = None;
|
||||
self._observe_active_editor = None;
|
||||
}
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn hide_setting(&self, _: &App) -> Option<HideStatusItem> {
|
||||
Some(HideStatusItem::new(|settings| {
|
||||
settings
|
||||
.status_bar
|
||||
.get_or_insert_default()
|
||||
.line_endings_button = Some(false);
|
||||
}))
|
||||
}
|
||||
}
|
||||
192
crates/line_ending_selector/src/line_ending_selector.rs
Normal file
192
crates/line_ending_selector/src/line_ending_selector.rs
Normal file
@@ -0,0 +1,192 @@
|
||||
mod line_ending_indicator;
|
||||
|
||||
use editor::Editor;
|
||||
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Task, WeakEntity, actions};
|
||||
use language::{Buffer, LineEnding};
|
||||
pub use line_ending_indicator::LineEndingIndicator;
|
||||
use picker::{Picker, PickerDelegate};
|
||||
use project::Project;
|
||||
use std::sync::Arc;
|
||||
use ui::{ListItem, ListItemSpacing, prelude::*};
|
||||
use util::ResultExt;
|
||||
use workspace::ModalView;
|
||||
|
||||
actions!(
|
||||
line_ending_selector,
|
||||
[
|
||||
/// Toggles the line ending selector modal.
|
||||
Toggle
|
||||
]
|
||||
);
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
cx.observe_new(LineEndingSelector::register).detach();
|
||||
}
|
||||
|
||||
pub struct LineEndingSelector {
|
||||
picker: Entity<Picker<LineEndingSelectorDelegate>>,
|
||||
}
|
||||
|
||||
impl LineEndingSelector {
|
||||
fn register(editor: &mut Editor, _window: Option<&mut Window>, cx: &mut Context<Editor>) {
|
||||
let editor_handle = cx.weak_entity();
|
||||
editor
|
||||
.register_action(move |_: &Toggle, window, cx| {
|
||||
Self::toggle(&editor_handle, window, cx);
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn toggle(editor: &WeakEntity<Editor>, window: &mut Window, cx: &mut App) {
|
||||
let Some((workspace, buffer)) = editor
|
||||
.update(cx, |editor, cx| {
|
||||
Some((editor.workspace()?, editor.active_buffer(cx)?))
|
||||
})
|
||||
.ok()
|
||||
.flatten()
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
workspace.update(cx, |workspace, cx| {
|
||||
let project = workspace.project().clone();
|
||||
workspace.toggle_modal(window, cx, move |window, cx| {
|
||||
LineEndingSelector::new(buffer, project, window, cx)
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
fn new(
|
||||
buffer: Entity<Buffer>,
|
||||
project: Entity<Project>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let line_ending = buffer.read(cx).line_ending();
|
||||
let delegate =
|
||||
LineEndingSelectorDelegate::new(cx.entity().downgrade(), buffer, project, line_ending);
|
||||
let picker = cx.new(|cx| Picker::nonsearchable_uniform_list(delegate, window, cx));
|
||||
Self { picker }
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for LineEndingSelector {
|
||||
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
|
||||
v_flex().w(rems(34.)).child(self.picker.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl Focusable for LineEndingSelector {
|
||||
fn focus_handle(&self, cx: &App) -> FocusHandle {
|
||||
self.picker.focus_handle(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<DismissEvent> for LineEndingSelector {}
|
||||
impl ModalView for LineEndingSelector {}
|
||||
|
||||
struct LineEndingSelectorDelegate {
|
||||
line_ending_selector: WeakEntity<LineEndingSelector>,
|
||||
buffer: Entity<Buffer>,
|
||||
project: Entity<Project>,
|
||||
line_ending: LineEnding,
|
||||
matches: Vec<LineEnding>,
|
||||
selected_index: usize,
|
||||
}
|
||||
|
||||
impl LineEndingSelectorDelegate {
|
||||
fn new(
|
||||
line_ending_selector: WeakEntity<LineEndingSelector>,
|
||||
buffer: Entity<Buffer>,
|
||||
project: Entity<Project>,
|
||||
line_ending: LineEnding,
|
||||
) -> Self {
|
||||
Self {
|
||||
line_ending_selector,
|
||||
buffer,
|
||||
project,
|
||||
line_ending,
|
||||
matches: vec![LineEnding::Unix, LineEnding::Windows],
|
||||
selected_index: 0,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PickerDelegate for LineEndingSelectorDelegate {
|
||||
type ListItem = ListItem;
|
||||
|
||||
fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
|
||||
"Select a line ending…".into()
|
||||
}
|
||||
|
||||
fn match_count(&self) -> usize {
|
||||
self.matches.len()
|
||||
}
|
||||
|
||||
fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
|
||||
if let Some(line_ending) = self.matches.get(self.selected_index) {
|
||||
self.buffer.update(cx, |this, cx| {
|
||||
this.set_line_ending(*line_ending, cx);
|
||||
});
|
||||
let buffer = self.buffer.clone();
|
||||
let project = self.project.clone();
|
||||
cx.defer(move |cx| {
|
||||
project.update(cx, |this, cx| {
|
||||
this.save_buffer(buffer, cx).detach();
|
||||
});
|
||||
});
|
||||
}
|
||||
self.dismissed(window, cx);
|
||||
}
|
||||
|
||||
fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
|
||||
self.line_ending_selector
|
||||
.update(cx, |_, cx| cx.emit(DismissEvent))
|
||||
.log_err();
|
||||
}
|
||||
|
||||
fn selected_index(&self) -> usize {
|
||||
self.selected_index
|
||||
}
|
||||
|
||||
fn set_selected_index(
|
||||
&mut self,
|
||||
ix: usize,
|
||||
_window: &mut Window,
|
||||
_: &mut Context<Picker<Self>>,
|
||||
) {
|
||||
self.selected_index = ix;
|
||||
}
|
||||
|
||||
fn update_matches(
|
||||
&mut self,
|
||||
_query: String,
|
||||
_window: &mut Window,
|
||||
_cx: &mut Context<Picker<Self>>,
|
||||
) -> gpui::Task<()> {
|
||||
return Task::ready(());
|
||||
}
|
||||
|
||||
fn render_match(
|
||||
&self,
|
||||
ix: usize,
|
||||
selected: bool,
|
||||
_: &mut Window,
|
||||
_: &mut Context<Picker<Self>>,
|
||||
) -> Option<Self::ListItem> {
|
||||
let line_ending = self.matches.get(ix)?;
|
||||
let label = line_ending.label();
|
||||
|
||||
let mut list_item = ListItem::new(ix)
|
||||
.inset(true)
|
||||
.spacing(ListItemSpacing::Sparse)
|
||||
.toggle_state(selected)
|
||||
.child(Label::new(label));
|
||||
|
||||
if &self.line_ending == line_ending {
|
||||
list_item = list_item.end_slot(Icon::new(IconName::Check).color(Color::Muted));
|
||||
}
|
||||
|
||||
Some(list_item)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user