feat: port GTK version fallbacks, helpgen, bench modes, CLI leftovers

- apprt-gtk: Ghostty's runtime+build-time version gating (at_least)
  with cargo features adw_1_3/adw_1_4/adw_1_5/gtk_4_16 (default on) and
  the fallbacks for older libraries: MessageDialog (adw < 1.5),
  clipboard dialog without SwitchRow, pre-Banner debug warning and
  child-exited widgets, the old about dialog, GTK 4.16 CSS error source
  bytes. The minimum stays GTK 4.14 / libadwaita 1.5 (Ghostty's floor).
  The root build links with -z lazy so version-gated symbols don't keep
  older libraries from loading.
- ghostty-helpgen (port of helpgen.zig) generates CLI action help and
  keybinding action docs from doc comments at build time, replacing the
  checked-in help/*.txt; +help and +list-actions --docs stay
  byte-identical to Ghostty 1.3.1.
- ghostty-bench: libc wcwidth mode and separate simd/table and
  uucode/table code paths like Zig.
- ghostty-cli: Windows branches of edit-config, list-colors,
  list-keybinds and boo; ssh-cache DiskCache flock locking (no-op on
  Windows, as in Zig) with an exclusivity test.

Test Plan:
- cargo test --workspace
- diff <(cargo run -q -- +list-actions --docs) \
  <(ghostty +list-actions --docs)
This commit is contained in:
ddidderr committed 2026-10-12 06:38:33 +02:00
1 parent cd86fe92a8
commit f2252bb165
51 files changed
+2426 -1621

No files matched your search

@@ -15,7 +15,10 @@ use ghostty_apprt::structs::ClipboardRequest;
use ghostty_os::i18n::tr;
use gtk::{glib, glib::subclass::Signal};
use super::dialog::{Dialog, DialogImpl};
use super::dialog::{Dialog, dialog_subclass_wrapper, impl_dialog_parent};
/// Whether we're able to have the remember switch (Zig: `can_remember`).
pub const CAN_REMEMBER: bool = crate::adw_version::COMPTIME_SUPPORTS_SWITCH_ROW;
/// GBoxed wrapper for [`ClipboardRequest`] (Zig: boxed `apprt.ClipboardRequest`).
#[derive(Debug, Clone, Copy, PartialEq, Eq, glib::Boxed)]
@@ -27,13 +30,10 @@ mod imp {
use gtk::{glib, subclass::prelude::*};
use super::{
AdwAlertDialogImpl,
AdwDialogImpl,
Cell,
ClipboardRequest,
ClipboardRequestBoxed,
Dialog,
DialogImpl,
ObjectImpl,
ObjectSubclass,
OnceLock,
@@ -45,7 +45,14 @@ mod imp {
#[derive(Default, glib::Properties, gtk::CompositeTemplate)]
#[properties(wrapper_type = super::ClipboardConfirmationDialog)]
#[template(resource = "/com/mitchellh/ghostty/ui/1.4/clipboard-confirmation-dialog.ui")]
#[cfg_attr(
feature = "adw_1_4",
template(resource = "/com/mitchellh/ghostty/ui/1.4/clipboard-confirmation-dialog.ui")
)]
#[cfg_attr(
not(feature = "adw_1_4"),
template(resource = "/com/mitchellh/ghostty/ui/1.0/clipboard-confirmation-dialog.ui")
)]
pub struct ClipboardConfirmationDialog {
/// The request that this dialog is for.
#[property(get, set, nullable)]
@@ -71,6 +78,8 @@ mod imp {
pub reveal_button: TemplateChild<gtk::Button>,
#[template_child]
pub hide_button: TemplateChild<gtk::Button>,
// Only exists in the 1.4+ template (`adw::SwitchRow`).
#[cfg(feature = "adw_1_4")]
#[template_child]
pub remember_choice: TemplateChild<adw::SwitchRow>,
}
@@ -190,26 +199,24 @@ mod imp {
}
impl WidgetImpl for ClipboardConfirmationDialog {}
impl AdwDialogImpl for ClipboardConfirmationDialog {}
impl AdwAlertDialogImpl for ClipboardConfirmationDialog {
fn response(&self, response: &str) {
let remember = self.remember_choice.is_active();
let obj = self.obj();
if response == "cancel" {
obj.emit_by_name::<()>("deny", &[&remember]);
} else if response == "ok" {
obj.emit_by_name::<()>("confirm", &[&remember]);
}
super::impl_dialog_parent!(ClipboardConfirmationDialog, |this, response| {
#[cfg(feature = "adw_1_4")]
let remember = this.remember_choice.is_active();
#[cfg(not(feature = "adw_1_4"))]
let remember = false;
let obj = this.obj();
if response == "cancel" {
obj.emit_by_name::<()>("deny", &[&remember]);
} else if response == "ok" {
obj.emit_by_name::<()>("confirm", &[&remember]);
}
}
impl DialogImpl for ClipboardConfirmationDialog {}
});
}
glib::wrapper! {
pub struct ClipboardConfirmationDialog(ObjectSubclass<imp::ClipboardConfirmationDialog>)
@extends Dialog, adw::AlertDialog, adw::Dialog, gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::ShortcutManager;
}
dialog_subclass_wrapper!(
ClipboardConfirmationDialog,
imp::ClipboardConfirmationDialog
);
impl ClipboardConfirmationDialog {
pub fn new() -> Self {
@@ -8,7 +8,18 @@ use adw::{prelude::*, subclass::prelude::*};
use ghostty_os::i18n::tr;
use gtk::{glib, glib::subclass::Signal};
use super::dialog::{Dialog, DialogImpl};
use super::dialog::{Dialog, dialog_subclass_wrapper, impl_dialog_parent};
/// Name of the "user confirmed the close" signal. Zig always uses
/// `close-request`, but with the `adw::MessageDialog` parent (a `GtkWindow`)
/// that name already exists in the class ancestry, which makes GObject
/// refuse to register it (and would fire it on every window close). Zig's
/// fallback hits that, so the fallback build renames the signal.
const CLOSE_REQUEST: &str = if cfg!(feature = "adw_1_5") {
"close-request"
} else {
"dialog-close-request"
};
/// The target of a close dialog (`GhosttyCloseConfirmationDialogTarget`).
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, glib::Enum)]
@@ -47,19 +58,7 @@ impl Target {
mod imp {
use gtk::{glib, glib::prelude::*, subclass::prelude::*};
use super::{
AdwAlertDialogImpl,
AdwDialogImpl,
Cell,
Dialog,
DialogImpl,
ObjectImpl,
ObjectSubclass,
OnceLock,
Signal,
Target,
WidgetImpl,
};
use super::{Cell, Dialog, ObjectImpl, ObjectSubclass, OnceLock, Signal, Target, WidgetImpl};
#[derive(Default, glib::Properties, gtk::CompositeTemplate)]
#[properties(wrapper_type = super::CloseConfirmationDialog)]
@@ -91,7 +90,7 @@ mod imp {
static SIGNALS: OnceLock<Vec<Signal>> = OnceLock::new();
SIGNALS.get_or_init(|| {
vec![
Signal::builder("close-request").build(),
Signal::builder(super::CLOSE_REQUEST).build(),
Signal::builder("cancel").build(),
]
})
@@ -102,25 +101,17 @@ mod imp {
}
}
impl WidgetImpl for CloseConfirmationDialog {}
impl AdwDialogImpl for CloseConfirmationDialog {}
impl AdwAlertDialogImpl for CloseConfirmationDialog {
fn response(&self, response: &str) {
let name = if response == "close" {
"close-request"
} else {
"cancel"
};
self.obj().emit_by_name::<()>(name, &[]);
}
}
impl DialogImpl for CloseConfirmationDialog {}
super::impl_dialog_parent!(CloseConfirmationDialog, |this, response| {
let name = if response == "close" {
super::CLOSE_REQUEST
} else {
"cancel"
};
this.obj().emit_by_name::<()>(name, &[]);
});
}
glib::wrapper! {
pub struct CloseConfirmationDialog(ObjectSubclass<imp::CloseConfirmationDialog>)
@extends Dialog, adw::AlertDialog, adw::Dialog, gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::ShortcutManager;
}
dialog_subclass_wrapper!(CloseConfirmationDialog, imp::CloseConfirmationDialog);
impl CloseConfirmationDialog {
pub fn new(target: Target) -> Self {
@@ -142,7 +133,7 @@ impl CloseConfirmationDialog {
pub fn connect_close_request<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
self.connect_closure(
"close-request",
CLOSE_REQUEST,
false,
glib::closure_local!(move |obj: &Self| f(obj)),
)
@@ -9,18 +9,15 @@ use gtk::{glib, glib::subclass::Signal};
use super::{
config::Config,
dialog::{Dialog, DialogImpl},
dialog::{Dialog, dialog_subclass_wrapper, impl_dialog_parent},
};
mod imp {
use gtk::{glib, glib::prelude::*, subclass::prelude::*};
use super::{
AdwAlertDialogImpl,
AdwDialogImpl,
Config,
Dialog,
DialogImpl,
ObjectImpl,
ObjectSubclass,
ObjectSubclassExt,
@@ -69,23 +66,15 @@ mod imp {
}
}
impl WidgetImpl for ConfigErrorsDialog {}
impl AdwDialogImpl for ConfigErrorsDialog {}
impl AdwAlertDialogImpl for ConfigErrorsDialog {
fn response(&self, response: &str) {
if response != "reload" {
return;
}
self.obj().emit_by_name::<()>("reload-config", &[]);
super::impl_dialog_parent!(ConfigErrorsDialog, |this, response| {
if response != "reload" {
return;
}
}
impl DialogImpl for ConfigErrorsDialog {}
this.obj().emit_by_name::<()>("reload-config", &[]);
});
}
glib::wrapper! {
pub struct ConfigErrorsDialog(ObjectSubclass<imp::ConfigErrorsDialog>)
@extends Dialog, adw::AlertDialog, adw::Dialog, gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::ShortcutManager;
}
dialog_subclass_wrapper!(ConfigErrorsDialog, imp::ConfigErrorsDialog);
impl ConfigErrorsDialog {
pub fn new(config: &Config) -> Self {
@@ -1,5 +1,9 @@
//! Port of apprt/gtk/class/debug_warning.zig (`GhosttyDebugWarning`).
//! Uses the `adw::Banner` variant (libadwaita >= 1.3).
//!
//! Debug warning banner. It is based on `adw::Banner` (a `adw::Bin` with the
//! 1.3 template) if we're using Adwaita 1.3 or newer (the `adw_1_3` build
//! level, Zig: `adw_version.supportsBanner()`). Otherwise it is a `gtk::Box`
//! holding a label (1.2 template).
use gtk::glib;
@@ -7,14 +11,24 @@ mod imp {
use gtk::{glib, subclass::prelude::*};
#[derive(Default, gtk::CompositeTemplate)]
#[template(resource = "/com/mitchellh/ghostty/ui/1.3/debug-warning.ui")]
#[cfg_attr(
feature = "adw_1_3",
template(resource = "/com/mitchellh/ghostty/ui/1.3/debug-warning.ui")
)]
#[cfg_attr(
not(feature = "adw_1_3"),
template(resource = "/com/mitchellh/ghostty/ui/1.2/debug-warning.ui")
)]
pub struct DebugWarning;
#[glib::object_subclass]
impl ObjectSubclass for DebugWarning {
const NAME: &'static str = "GhosttyDebugWarning";
type Type = super::DebugWarning;
#[cfg(feature = "adw_1_3")]
type ParentType = adw::Bin;
#[cfg(not(feature = "adw_1_3"))]
type ParentType = gtk::Box;
fn class_init(klass: &mut Self::Class) {
klass.bind_template();
@@ -31,15 +45,26 @@ mod imp {
}
}
impl WidgetImpl for DebugWarning {}
#[cfg(feature = "adw_1_3")]
impl adw::subclass::prelude::BinImpl for DebugWarning {}
#[cfg(not(feature = "adw_1_3"))]
impl BoxImpl for DebugWarning {}
}
#[cfg(feature = "adw_1_3")]
glib::wrapper! {
pub struct DebugWarning(ObjectSubclass<imp::DebugWarning>)
@extends adw::Bin, gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
}
#[cfg(not(feature = "adw_1_3"))]
glib::wrapper! {
pub struct DebugWarning(ObjectSubclass<imp::DebugWarning>)
@extends gtk::Box, gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Orientable;
}
impl DebugWarning {
pub fn new() -> Self {
crate::register_resources();
+119 -8
View File
@@ -1,12 +1,25 @@
//! Port of apprt/gtk/class/dialog.zig (`GhosttyDialog`).
//!
//! Thin `adw::AlertDialog` subclass (we require libadwaita >= 1.5 so the
//! `adw::MessageDialog` fallback is not needed). Subclasses implement
//! [`DialogImpl`] and may override `response` via `AlertDialogImpl`.
//! Thin abstraction over `adw::AlertDialog` and `adw::MessageDialog`, chosen
//! at compile time from the libadwaita build level exactly like Zig's
//! `Parent = if (adw_version.supportsDialogs()) adw.AlertDialog else
//! adw.MessageDialog` (the `adw_1_5` cargo feature plays the role of the
//! header version). Subclasses implement [`DialogImpl`] and override
//! `response` through [`impl_dialog_parent!`].
//!
//! Once we drop support for Adwaita <= 1.2, this can be fully removed and we
//! can use `adw::AlertDialog` directly.
use adw::{prelude::*, subclass::prelude::*};
use gtk::glib;
/// The parent class of [`Dialog`] (Zig: `Dialog.Parent`).
#[cfg(feature = "adw_1_5")]
pub type DialogParent = adw::AlertDialog;
/// The parent class of [`Dialog`] (Zig: `Dialog.Parent`).
#[cfg(not(feature = "adw_1_5"))]
pub type DialogParent = adw::MessageDialog;
mod imp {
use adw::subclass::prelude::*;
use gtk::glib;
@@ -18,46 +31,144 @@ mod imp {
impl ObjectSubclass for Dialog {
const NAME: &'static str = "GhosttyDialog";
type Type = super::Dialog;
type ParentType = adw::AlertDialog;
type ParentType = super::DialogParent;
}
impl ObjectImpl for Dialog {}
impl WidgetImpl for Dialog {}
#[cfg(feature = "adw_1_5")]
impl AdwDialogImpl for Dialog {}
#[cfg(feature = "adw_1_5")]
impl AdwAlertDialogImpl for Dialog {}
#[cfg(not(feature = "adw_1_5"))]
impl WindowImpl for Dialog {}
#[cfg(not(feature = "adw_1_5"))]
impl MessageDialogImpl for Dialog {}
}
#[cfg(feature = "adw_1_5")]
glib::wrapper! {
pub struct Dialog(ObjectSubclass<imp::Dialog>)
@extends adw::AlertDialog, adw::Dialog, gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::ShortcutManager;
}
#[cfg(not(feature = "adw_1_5"))]
glib::wrapper! {
pub struct Dialog(ObjectSubclass<imp::Dialog>)
@extends adw::MessageDialog, gtk::Window, gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget, gtk::Native,
gtk::Root, gtk::ShortcutManager;
}
/// Implemented by subclasses of [`Dialog`].
#[cfg(feature = "adw_1_5")]
pub trait DialogImpl: AdwAlertDialogImpl {}
/// Implemented by subclasses of [`Dialog`].
#[cfg(not(feature = "adw_1_5"))]
pub trait DialogImpl: MessageDialogImpl {}
#[allow(unsafe_code)]
// SAFETY: `Dialog` adds no class/instance data of its own; we simply forward
// to the parent (`adw::AlertDialog`) class/instance initialization, which is
// valid for any `T: DialogImpl` (a subtype of `AdwAlertDialogImpl`).
// to the parent class/instance initialization, which is valid for any
// `T: DialogImpl` (a subtype of the parent's `*Impl` trait).
unsafe impl<T: DialogImpl> IsSubclassable<T> for Dialog {
fn class_init(class: &mut glib::Class<Self>) {
<adw::AlertDialog as IsSubclassable<T>>::class_init(class.upcast_ref_mut());
<DialogParent as IsSubclassable<T>>::class_init(class.upcast_ref_mut());
}
fn instance_init(instance: &mut glib::subclass::InitializingObject<T>) {
<adw::AlertDialog as IsSubclassable<T>>::instance_init(instance);
<DialogParent as IsSubclassable<T>>::instance_init(instance);
}
}
impl Dialog {
/// Present the dialog over `parent`.
#[cfg(feature = "adw_1_5")]
pub fn present(&self, parent: Option<&impl IsA<gtk::Widget>>) {
adw::prelude::AdwDialogExt::present(self, parent);
}
/// Present the dialog over `parent`.
#[cfg(not(feature = "adw_1_5"))]
pub fn present(&self, parent: Option<&impl IsA<gtk::Widget>>) {
// Reset the previous parent window
self.set_transient_for(None::<&gtk::Window>);
// We need to get the window for the parent in order to set the
// transient-for property on the MessageDialog.
if let Some(window) = parent
.and_then(gtk::prelude::WidgetExt::root)
.and_then(|root| root.downcast::<gtk::Window>().ok())
{
self.set_transient_for(Some(&window));
}
gtk::prelude::GtkWindowExt::present(self);
}
/// Close the dialog without emitting `close-attempt`.
#[cfg(feature = "adw_1_5")]
pub fn close(&self) {
self.force_close();
}
/// Close the dialog.
#[cfg(not(feature = "adw_1_5"))]
pub fn close(&self) {
gtk::prelude::GtkWindowExt::close(self);
}
}
/// Declares the `glib::wrapper!` of a [`Dialog`] subclass with the right
/// `@extends` chain for the libadwaita build level.
macro_rules! dialog_subclass_wrapper {
($name:ident, $imp:ty) => {
#[cfg(feature = "adw_1_5")]
glib::wrapper! {
pub struct $name(ObjectSubclass<$imp>)
@extends $crate::class::dialog::Dialog, adw::AlertDialog, adw::Dialog, gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget,
gtk::ShortcutManager;
}
#[cfg(not(feature = "adw_1_5"))]
glib::wrapper! {
pub struct $name(ObjectSubclass<$imp>)
@extends $crate::class::dialog::Dialog, adw::MessageDialog, gtk::Window,
gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget,
gtk::Native, gtk::Root, gtk::ShortcutManager;
}
};
}
/// Implements the parent-class traits of a [`Dialog`] subclass (the
/// `Adw{Dialog,AlertDialog}Impl` or `{Window,MessageDialog}Impl` pair),
/// overriding `response` with `|this, response| body`.
macro_rules! impl_dialog_parent {
($ty:ty, |$this:ident, $response:ident| $body:block) => {
#[cfg(feature = "adw_1_5")]
impl adw::subclass::prelude::AdwDialogImpl for $ty {}
#[cfg(feature = "adw_1_5")]
impl adw::subclass::prelude::AdwAlertDialogImpl for $ty {
fn response(&self, $response: &str) {
let $this = self;
$body
}
}
#[cfg(not(feature = "adw_1_5"))]
impl gtk::subclass::prelude::WindowImpl for $ty {}
#[cfg(not(feature = "adw_1_5"))]
impl adw::subclass::prelude::MessageDialogImpl for $ty {
fn response(&self, $response: &str) {
let $this = self;
$body
}
}
impl $crate::class::dialog::DialogImpl for $ty {}
};
}
pub(crate) use dialog_subclass_wrapper;
pub(crate) use impl_dialog_parent;
@@ -1371,6 +1371,13 @@ impl Surface {
self.imp().child_exited.set(true);
self.notify("child-exited");
// If we have the noop child exited overlay then we don't do anything
// for child exited. The false return will force libghostty to show
// the normal text-based message.
if SurfaceChildExited::NOOP {
return false;
}
self.imp().child_exited_overlay.set_child_exited(Some(data));
true
}
@@ -1,26 +1,41 @@
//! Port of apprt/gtk/class/surface_child_exited.zig.
//!
//! Child exited overlay based on `adw.Banner` (Adwaita 1.3+; the crate
//! requires 1.5 so the Zig `SurfaceChildExitedNoop` fallback is omitted).
//! Emits `close-request` when the banner button is clicked.
//! Child exited overlay based on `adw.Banner` (Adwaita 1.3+, the `adw_1_3`
//! build level; emits `close-request` when the banner button is clicked).
//! Without it Zig compiles `SurfaceChildExitedNoop`, an empty widget that
//! immediately asks to be closed (`NOOP == true`); so does this port.
#[cfg(feature = "adw_1_3")]
use std::sync::OnceLock;
#[cfg(feature = "adw_1_3")]
use adw::{prelude::*, subclass::prelude::*};
use ghostty_apprt::surface::ChildExited;
use gtk::{glib, glib::subclass::Signal};
use gtk::glib;
#[cfg(feature = "adw_1_3")]
use gtk::glib::subclass::Signal;
#[cfg(not(feature = "adw_1_3"))]
use gtk::prelude::*;
/// Boxed wrapper so `ChildExited` can be a GObject property.
#[derive(Debug, Clone, Copy, PartialEq, Eq, glib::Boxed)]
#[boxed_type(name = "GhosttyChildExited", nullable)]
pub struct ChildExitedBoxed(pub ChildExited);
#[cfg(feature = "adw_1_3")]
glib::wrapper! {
pub struct SurfaceChildExited(ObjectSubclass<imp::SurfaceChildExited>)
@extends adw::Bin, gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
}
#[cfg(not(feature = "adw_1_3"))]
glib::wrapper! {
pub struct SurfaceChildExited(ObjectSubclass<imp::SurfaceChildExited>)
@extends gtk::Widget,
@implements gtk::Accessible, gtk::Buildable, gtk::ConstraintTarget;
}
impl Default for SurfaceChildExited {
fn default() -> Self {
Self::new()
@@ -28,16 +43,27 @@ impl Default for SurfaceChildExited {
}
impl SurfaceChildExited {
/// Can be detected at compile time (Zig: `pub const noop = true`).
pub const NOOP: bool = !cfg!(feature = "adw_1_3");
#[must_use]
pub fn new() -> Self {
glib::Object::new()
}
/// Set the child exited data (Zig: `setData`).
#[cfg(feature = "adw_1_3")]
pub fn set_child_exited(&self, data: Option<&ChildExited>) {
self.set_data(data.map(|d| ChildExitedBoxed(*d)));
}
/// Set the child exited data (Zig: `setData`); the noop widget just asks
/// to be closed.
#[cfg(not(feature = "adw_1_3"))]
pub fn set_child_exited(&self, _data: Option<&ChildExited>) {
self.emit_by_name::<()>("close-request", &[]);
}
pub fn connect_close_request<F: Fn(&Self) + 'static>(&self, f: F) -> glib::SignalHandlerId {
self.connect_closure(
"close-request",
@@ -47,6 +73,7 @@ impl SurfaceChildExited {
}
}
#[cfg(feature = "adw_1_3")]
mod imp {
use std::cell::RefCell;
@@ -141,3 +168,31 @@ mod imp {
impl WidgetImpl for SurfaceChildExited {}
impl BinImpl for SurfaceChildExited {}
}
/// Empty widget that does nothing if we don't have a new enough Adwaita
/// version to support the child exited banner.
#[cfg(not(feature = "adw_1_3"))]
mod imp {
use std::sync::OnceLock;
use gtk::{glib, glib::subclass::Signal, subclass::prelude::*};
#[derive(Default)]
pub struct SurfaceChildExited;
#[glib::object_subclass]
impl ObjectSubclass for SurfaceChildExited {
const NAME: &'static str = "GhosttySurfaceChildExited";
type Type = super::SurfaceChildExited;
type ParentType = gtk::Widget;
}
impl ObjectImpl for SurfaceChildExited {
fn signals() -> &'static [Signal] {
static SIGNALS: OnceLock<Vec<Signal>> = OnceLock::new();
SIGNALS.get_or_init(|| vec![Signal::builder("close-request").build()])
}
}
impl WidgetImpl for SurfaceChildExited {}
}
+23 -12
View File
@@ -1657,18 +1657,29 @@ impl Window {
let icon = "com.mitchellh.ghostty";
let website = "https://ghostty.org";
// libadwaita >= 1.5 is assumed, so AdwAboutDialog is always used
// (the Zig GtkAboutDialog fallback is unreachable here).
let _ = adw_version::supports_dialogs();
let dialog = adw::AboutDialog::builder()
.application_name(name)
.developer_name(tr("Ghostty Developers"))
.application_icon(icon)
.version(env!("CARGO_PKG_VERSION"))
.issue_url("https://github.com/ghostty-org/ghostty/issues")
.website(website)
.build();
dialog.present(Some(self));
if adw_version::supports_dialogs() {
let dialog = adw::AboutDialog::builder()
.application_name(name)
.developer_name(tr("Ghostty Developers"))
.application_icon(icon)
.version(env!("CARGO_PKG_VERSION"))
.issue_url("https://github.com/ghostty-org/ghostty/issues")
.website(website)
.build();
dialog.present(Some(self));
} else {
// libadwaita < 1.5 has no AdwAboutDialog: use the GTK one.
gtk::show_about_dialog(
Some(self),
&[
("program-name", &name),
("logo-icon-name", &icon),
("title", &tr("About Ghostty")),
("version", &env!("CARGO_PKG_VERSION")),
("website", &website),
],
);
}
}
fn action_close_tab(&self, param: Option<&glib::Variant>) {