Improve styling.
This commit is contained in:
+82
-3
@@ -18,12 +18,18 @@ const Layouts = @import("Layouts.zig");
|
||||
const OpenLayoutDialog = @import("OpenLayoutDialog.zig");
|
||||
const Pane = @import("Pane.zig");
|
||||
const SaveLayoutDialog = @import("SaveLayoutDialog.zig");
|
||||
const SettingsDialog = @import("SettingsDialog.zig");
|
||||
const Terminal = @import("Terminal.zig");
|
||||
const View = @import("View.zig");
|
||||
const appearance = @import("appearance.zig");
|
||||
|
||||
const Window = @This();
|
||||
|
||||
const sidebar_width = 220;
|
||||
/// Total width of the sidebar column, margins and border included — GTK folds
|
||||
/// both into a widget's size request. Widened when the sidebar became an inset
|
||||
/// card: the margin, border and roomier row padding all come out of the label,
|
||||
/// and at the old 220 a tab name truncated a good deal earlier than it used to.
|
||||
const sidebar_width = 236;
|
||||
|
||||
alloc: std.mem.Allocator,
|
||||
window: *adw.ApplicationWindow,
|
||||
@@ -147,6 +153,7 @@ pub fn create(alloc: std.mem.Allocator, app: *adw.Application) !*Window {
|
||||
header.as(gtk.Widget).addCssClass("flat");
|
||||
|
||||
const new_tab_button = gtk.Button.newFromIconName("tab-new-symbolic");
|
||||
new_tab_button.as(gtk.Widget).addCssClass("playpen-header-button");
|
||||
new_tab_button.as(gtk.Widget).setTooltipText("New tab (Ctrl+Shift+T)");
|
||||
_ = gtk.Button.signals.clicked.connect(
|
||||
new_tab_button,
|
||||
@@ -155,17 +162,22 @@ pub fn create(alloc: std.mem.Allocator, app: *adw.Application) !*Window {
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
header.packEnd(new_tab_button.as(gtk.Widget));
|
||||
// Packed at the start, with the window controls left alone at the end. In
|
||||
// a 220px header the two used to sit right up against the close button,
|
||||
// which both wasted the empty half of the bar and put "new tab" a few
|
||||
// pixels from "close window".
|
||||
header.packStart(new_tab_button.as(gtk.Widget));
|
||||
|
||||
// Layouts sit behind their own button rather than replacing the plain
|
||||
// new-tab one: opening an ordinary shell stays a single click.
|
||||
const layout_button = gtk.MenuButton.new();
|
||||
layout_button.setIconName("view-grid-symbolic");
|
||||
layout_button.as(gtk.Widget).addCssClass("playpen-header-button");
|
||||
layout_button.as(gtk.Widget).setTooltipText("Open a saved layout");
|
||||
layout_button.setPopover(self.layout_popover);
|
||||
self.layout_popover.as(gtk.Widget).addCssClass("playpen-layout-popover");
|
||||
self.refreshLayoutMenu();
|
||||
header.packEnd(layout_button.as(gtk.Widget));
|
||||
header.packStart(layout_button.as(gtk.Widget));
|
||||
sidebar.append(header.as(gtk.Widget));
|
||||
|
||||
self.list.setSelectionMode(.single);
|
||||
@@ -185,6 +197,28 @@ pub fn create(alloc: std.mem.Allocator, app: *adw.Application) !*Window {
|
||||
scroller.setChild(self.list.as(gtk.Widget));
|
||||
sidebar.append(scroller.as(gtk.Widget));
|
||||
|
||||
// Settings sit at the foot of the sidebar rather than in its header. The
|
||||
// header holds the two things you reach for constantly — a new tab and a
|
||||
// saved layout — and a preferences button is the opposite of that: opened
|
||||
// rarely, and never in a hurry. Below the tab list it stays out of the way
|
||||
// of both, and it is where every other sidebar puts it.
|
||||
const footer = gtk.Box.new(.horizontal, 0);
|
||||
footer.as(gtk.Widget).addCssClass("playpen-sidebar-footer");
|
||||
|
||||
const settings_button = gtk.Button.newFromIconName("emblem-system-symbolic");
|
||||
settings_button.as(gtk.Widget).addCssClass("flat");
|
||||
settings_button.as(gtk.Widget).addCssClass("playpen-settings-button");
|
||||
settings_button.as(gtk.Widget).setTooltipText("Settings (Ctrl+,)");
|
||||
_ = gtk.Button.signals.clicked.connect(
|
||||
settings_button,
|
||||
*Window,
|
||||
&onSettingsClicked,
|
||||
self,
|
||||
.{},
|
||||
);
|
||||
footer.append(settings_button.as(gtk.Widget));
|
||||
sidebar.append(footer.as(gtk.Widget));
|
||||
|
||||
// ---- content -------------------------------------------------------
|
||||
self.stack.as(gtk.Widget).setHexpand(1);
|
||||
self.stack.as(gtk.Widget).setVexpand(1);
|
||||
@@ -220,6 +254,8 @@ pub fn create(alloc: std.mem.Allocator, app: *adw.Application) !*Window {
|
||||
.{},
|
||||
);
|
||||
|
||||
appearance.onChanged(&onAppearanceChanged, self);
|
||||
|
||||
try self.newTab();
|
||||
|
||||
return self;
|
||||
@@ -717,6 +753,40 @@ fn closeTab(self: *Window, tab: *Tab) void {
|
||||
self.select(self.tabs.items[next]);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Settings
|
||||
|
||||
fn openSettings(self: *Window) void {
|
||||
SettingsDialog.present(self.alloc, self.window.as(gtk.Window)) catch |err| {
|
||||
std.log.err("failed to open settings: {s}", .{@errorName(err)});
|
||||
};
|
||||
}
|
||||
|
||||
fn onSettingsClicked(_: *gtk.Button, self: *Window) callconv(.c) void {
|
||||
self.openSettings();
|
||||
}
|
||||
|
||||
/// The colour scheme changed. Everything styled by CSS restyles itself; the
|
||||
/// terminal grids do not, because Cairo draws them from `theme.zig` and GTK
|
||||
/// has no idea that widget's contents depend on the palette at all. Without
|
||||
/// this, switching to light leaves every terminal a dark rectangle until
|
||||
/// something else happens to dirty it.
|
||||
///
|
||||
/// Sessions already open are re-palletted rather than left on the one they
|
||||
/// started in: a shell you have had running all day is exactly the one you are
|
||||
/// looking at when you switch, and leaving it in the old scheme's colours
|
||||
/// would make the setting look like it only applies to new tabs.
|
||||
fn onAppearanceChanged(ctx: ?*anyopaque) void {
|
||||
const self: *Window = @ptrCast(@alignCast(ctx.?));
|
||||
for (self.tabs.items) |tab| {
|
||||
for (tab.view.panes.items) |pane| {
|
||||
const terminal = pane.terminal() orelse continue;
|
||||
terminal.session.refreshPalette();
|
||||
terminal.area.as(gtk.Widget).queueDraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Signal handlers
|
||||
|
||||
@@ -813,6 +883,10 @@ fn onDestroy(_: *adw.ApplicationWindow, self: *Window) callconv(.c) void {
|
||||
if (self.closing) return;
|
||||
self.closing = true;
|
||||
|
||||
// Before anything is freed: a scheme change arriving mid-teardown would
|
||||
// otherwise walk a tab list we are about to destroy.
|
||||
appearance.clearOnChanged();
|
||||
|
||||
// Each terminal owns a session, which owns a PTY and its child process.
|
||||
// Dropping them here reaps the children rather than orphaning them.
|
||||
for (self.tabs.items) |tab| {
|
||||
@@ -943,8 +1017,13 @@ fn onShortcut(
|
||||
}
|
||||
|
||||
// Ctrl+PageUp/PageDown cycles tabs, matching most tabbed terminals.
|
||||
// Ctrl+comma opens settings, which is the convention nearly everywhere.
|
||||
if (ctrl and !shift) {
|
||||
switch (keyval) {
|
||||
gdk.KEY_comma => {
|
||||
self.openSettings();
|
||||
return 1;
|
||||
},
|
||||
gdk.KEY_Page_Up => {
|
||||
self.cycle(-1);
|
||||
return 1;
|
||||
|
||||
Reference in New Issue
Block a user