NAWS mid-session updates now propagate to the window stack: Application re-runs each open window's relayout on resize. Window#relayout re-centers (clamped to the screen edge) by shifting all descendants; MasterDetail recomputes its full geometry and re-renders the detail at the new width. ButtonBar placement is extracted into ButtonBar.place for reuse. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
107 lines
3.8 KiB
Ruby
107 lines
3.8 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
require_relative '../widgets'
|
|
|
|
module BBS
|
|
module Windows
|
|
# Lays out a row of buttons at the bottom of a window.
|
|
#
|
|
# Button descriptor:
|
|
# { key:, label:, on_click:, primary:, cancel:, align:, keep_open: }
|
|
#
|
|
# `on_click` receives the BBS::Window as its only argument and may return
|
|
# :keep to keep the window open. Any other return value (or nil) causes
|
|
# the window to be closed after the handler.
|
|
#
|
|
# `primary: true` gets initial focus on the button.
|
|
# `cancel: true` is informational only — ESC closes the window through the
|
|
# standard Window#close_key path. If you also want a cancel-specific side
|
|
# effect on ESC, set `window.on_close = ...` manually.
|
|
# `align: :left | :right | :center` — default is :center for a single
|
|
# button, :right otherwise. Left-aligned buttons stack from the left edge;
|
|
# right-aligned buttons stack flush against the right edge in declaration
|
|
# order (so the last right-aligned button sits closest to the edge).
|
|
module ButtonBar
|
|
module_function
|
|
|
|
# Returns { widgets:, primary:, by_key: }.
|
|
def attach(window, app, descriptors)
|
|
return { widgets: [], primary: nil, by_key: {} } if descriptors.nil? || descriptors.empty?
|
|
|
|
widgets = descriptors.map do |d|
|
|
BBS::Widgets::Button.new(label: d[:label], on_click: build_handler(window, app, d))
|
|
end
|
|
|
|
place(window, widgets, descriptors)
|
|
|
|
widgets.each { |w| window.add(w) }
|
|
|
|
primary_idx = descriptors.index { |d| d[:primary] }
|
|
primary = primary_idx ? widgets[primary_idx] : nil
|
|
|
|
by_key = {}
|
|
descriptors.each_with_index { |d, i| by_key[d[:key]] = widgets[i] if d[:key] }
|
|
|
|
{ widgets: widgets, primary: primary, by_key: by_key }
|
|
end
|
|
|
|
# Position (or re-position) already-built bar widgets against the
|
|
# window's current bounds. Used by attach and by window relayouts.
|
|
def place(window, widgets, descriptors)
|
|
return if widgets.empty?
|
|
|
|
default_align = descriptors.length == 1 ? :center : :right
|
|
aligns = descriptors.map { |d| d[:align] || default_align }
|
|
|
|
row_y = window.bounds.y + window.bounds.height - 2
|
|
left_x0 = window.bounds.x + 2
|
|
right_x1 = window.bounds.x + window.bounds.width - 2
|
|
win_x0 = window.bounds.x
|
|
win_w = window.bounds.width
|
|
|
|
x = left_x0
|
|
widgets.each_with_index do |w, i|
|
|
next unless aligns[i] == :left
|
|
bw = button_width(w)
|
|
w.layout(x, row_y, bw, 1)
|
|
x += bw + 1
|
|
end
|
|
|
|
right_indices = aligns.each_index.select { |i| aligns[i] == :right }
|
|
total_right = right_indices.sum { |i| button_width(widgets[i]) + 1 } - 1
|
|
total_right = [total_right, 0].max
|
|
x = right_x1 - total_right + 1
|
|
right_indices.each do |i|
|
|
bw = button_width(widgets[i])
|
|
widgets[i].layout(x, row_y, bw, 1)
|
|
x += bw + 1
|
|
end
|
|
|
|
center_indices = aligns.each_index.select { |i| aligns[i] == :center }
|
|
total_center = center_indices.sum { |i| button_width(widgets[i]) + 1 } - 1
|
|
total_center = [total_center, 0].max
|
|
x = win_x0 + (win_w - total_center) / 2
|
|
center_indices.each do |i|
|
|
bw = button_width(widgets[i])
|
|
widgets[i].layout(x, row_y, bw, 1)
|
|
x += bw + 1
|
|
end
|
|
end
|
|
|
|
def button_width(button)
|
|
"[ #{button.display_label} ]".length
|
|
end
|
|
|
|
def build_handler(window, app, descriptor)
|
|
on_click = descriptor[:on_click]
|
|
keep_open = descriptor[:keep_open]
|
|
lambda do |_btn|
|
|
result = on_click&.call(window)
|
|
app.close_window(window) unless keep_open || result == :keep
|
|
:handled
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|