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

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:
Mohamad Khani
2026-07-14 02:22:17 +03:30
commit b72a46db68
3984 changed files with 1583326 additions and 0 deletions

View File

@@ -0,0 +1,24 @@
[package]
name = "ui_prompt"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/ui_prompt.rs"
[features]
default = []
[dependencies]
gpui.workspace = true
markdown.workspace = true
menu.workspace = true
settings.workspace = true
theme_settings.workspace = true
ui.workspace = true
workspace.workspace = true

View File

@@ -0,0 +1 @@
../../LICENSE-GPL

View File

@@ -0,0 +1,205 @@
use gpui::{
App, Entity, EventEmitter, FocusHandle, Focusable, PromptButton, PromptHandle, PromptLevel,
PromptResponse, RenderablePromptHandle, SharedString, TextStyleRefinement, Window, div,
prelude::*,
};
use markdown::{Markdown, MarkdownElement, MarkdownStyle};
use settings::{Settings, SettingsStore};
use theme_settings::ThemeSettings;
use ui::{FluentBuilder, TintColor, prelude::*};
use workspace::WorkspaceSettings;
pub fn init(cx: &mut App) {
process_settings(cx);
cx.observe_global::<SettingsStore>(process_settings)
.detach();
}
fn process_settings(cx: &mut App) {
let settings = WorkspaceSettings::get_global(cx);
if settings.use_system_prompts && cfg!(not(any(target_os = "linux", target_os = "freebsd"))) {
cx.reset_prompt_builder();
} else {
cx.set_prompt_builder(zed_prompt_renderer);
}
}
/// Use this function in conjunction with [App::set_prompt_builder] to force
/// GPUI to use the internal prompt system.
fn zed_prompt_renderer(
level: PromptLevel,
message: &str,
detail: Option<&str>,
actions: &[PromptButton],
handle: PromptHandle,
window: &mut Window,
cx: &mut App,
) -> RenderablePromptHandle {
let renderer = cx.new({
|cx| ZedPromptRenderer {
_level: level,
message: cx.new(|cx| Markdown::new(SharedString::new(message), None, None, cx)),
actions: actions.iter().map(|a| a.label().to_string()).collect(),
focus: cx.focus_handle(),
active_action_id: 0,
detail: detail
.filter(|text| !text.is_empty())
.map(|text| cx.new(|cx| Markdown::new(SharedString::new(text), None, None, cx))),
}
});
handle.with_view(renderer, window, cx)
}
pub struct ZedPromptRenderer {
_level: PromptLevel,
message: Entity<Markdown>,
actions: Vec<String>,
focus: FocusHandle,
active_action_id: usize,
detail: Option<Entity<Markdown>>,
}
impl ZedPromptRenderer {
fn confirm(&mut self, _: &menu::Confirm, _window: &mut Window, cx: &mut Context<Self>) {
cx.emit(PromptResponse(self.active_action_id));
}
fn cancel(&mut self, _: &menu::Cancel, _window: &mut Window, cx: &mut Context<Self>) {
if let Some(ix) = self.actions.iter().position(|a| a == "Cancel") {
cx.emit(PromptResponse(ix));
}
}
fn select_first(
&mut self,
_: &menu::SelectFirst,
_window: &mut Window,
cx: &mut Context<Self>,
) {
self.active_action_id = self.actions.len().saturating_sub(1);
cx.notify();
}
fn select_last(&mut self, _: &menu::SelectLast, _window: &mut Window, cx: &mut Context<Self>) {
self.active_action_id = 0;
cx.notify();
}
fn select_next(&mut self, _: &menu::SelectNext, _window: &mut Window, cx: &mut Context<Self>) {
self.active_action_id = (self.active_action_id + 1) % self.actions.len();
cx.notify();
}
fn select_previous(
&mut self,
_: &menu::SelectPrevious,
_window: &mut Window,
cx: &mut Context<Self>,
) {
if self.active_action_id > 0 {
self.active_action_id -= 1;
} else {
self.active_action_id = self.actions.len().saturating_sub(1);
}
cx.notify();
}
}
impl Render for ZedPromptRenderer {
fn render(&mut self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let settings = ThemeSettings::get_global(cx);
let dialog = v_flex()
.key_context("Prompt")
.cursor_default()
.track_focus(&self.focus)
.on_action(cx.listener(Self::confirm))
.on_action(cx.listener(Self::cancel))
.on_action(cx.listener(Self::select_next))
.on_action(cx.listener(Self::select_previous))
.on_action(cx.listener(Self::select_first))
.on_action(cx.listener(Self::select_last))
.w_80()
.p_4()
.gap_4()
.elevation_3(cx)
.overflow_hidden()
.font_family(settings.ui_font.family.clone())
.child(div().w_full().child(MarkdownElement::new(
self.message.clone(),
markdown_style(true, window, cx),
)))
.children(self.detail.clone().map(|detail| {
div().w_full().text_xs().child(MarkdownElement::new(
detail,
markdown_style(false, window, cx),
))
}))
.child(
v_flex()
.gap_1()
.children(self.actions.iter().enumerate().map(|(ix, action)| {
Button::new(ix, action.clone())
.full_width()
.style(ButtonStyle::Outlined)
.when(ix == self.active_action_id, |s| {
s.style(ButtonStyle::Tinted(TintColor::Accent))
})
.tab_index(ix as isize)
.on_click(cx.listener(move |_, _, _window, cx| {
cx.emit(PromptResponse(ix));
}))
})),
);
div()
.size_full()
.occlude()
.bg(gpui::black().opacity(0.2))
.child(
v_flex()
.size_full()
.absolute()
.top_0()
.left_0()
.items_center()
.justify_center()
.child(dialog),
)
}
}
fn markdown_style(main_message: bool, window: &Window, cx: &App) -> MarkdownStyle {
let mut base_text_style = window.text_style();
let settings = ThemeSettings::get_global(cx);
let font_size = settings.ui_font_size(cx).into();
let color = if main_message {
Color::Default.color(cx)
} else {
Color::Muted.color(cx)
};
base_text_style.refine(&TextStyleRefinement {
font_family: Some(settings.ui_font.family.clone()),
font_size: Some(font_size),
color: Some(color),
..Default::default()
});
MarkdownStyle {
base_text_style,
selection_background_color: cx.theme().colors().element_selection_background,
..Default::default()
}
}
impl EventEmitter<PromptResponse> for ZedPromptRenderer {}
impl Focusable for ZedPromptRenderer {
fn focus_handle(&self, _: &crate::App) -> FocusHandle {
self.focus.clone()
}
}