new framework

This commit is contained in:
2026-05-12 22:15:07 +02:00
parent be21826ecc
commit 953498a62d
24 changed files with 2410 additions and 180 deletions

114
lib/bbs/dialogs.rb Normal file
View File

@@ -0,0 +1,114 @@
# frozen_string_literal: true
require_relative 'window'
require_relative 'widgets'
module BBS
module Dialogs
# Build a centred Window with the given inner size.
def self.centred_window(app, width:, height:, title: nil)
cols = app.cols
rows = app.rows
w = [width, cols - 4].min
h = [height, rows - 4].min
x = (cols - w) / 2 + 1
y = (rows - h) / 2 + 1
win = Window.new(title: title, modal: true)
win.layout(x, y, w, h)
win
end
# Show a one-line message with an OK button.
def self.message(app, text, title: ' Message ', width: nil)
lines = wrap(text, 50)
w = (width || ([lines.map(&:length).max + 6, 30].max))
h = lines.size + 5
window = centred_window(app, width: w, height: h, title: title)
lines.each_with_index do |line, i|
label = Widgets::Label.new(text: line, style_key: :window_text)
label.layout(window.bounds.x + 2, window.bounds.y + 1 + i, w - 4, 1)
window.add(label)
end
btn = Widgets::Button.new(label: '&OK', on_click: ->(_) { app.close_window(window) })
btn.layout(window.bounds.x + (w - 8) / 2, window.bounds.y + h - 2, 8, 1)
window.add(btn)
window.reset_focus
app.open_window(window)
window
end
# Yes/No confirmation. Calls on_yes / on_no.
def self.confirm(app, text, title: ' Confirm ', on_yes: nil, on_no: nil)
lines = wrap(text, 50)
w = [lines.map(&:length).max + 6, 36].max
h = lines.size + 5
window = centred_window(app, width: w, height: h, title: title)
lines.each_with_index do |line, i|
label = Widgets::Label.new(text: line, style_key: :window_text)
label.layout(window.bounds.x + 2, window.bounds.y + 1 + i, w - 4, 1)
window.add(label)
end
yes = Widgets::Button.new(label: '&Yes',
on_click: ->(_) { app.close_window(window); on_yes&.call })
no = Widgets::Button.new(label: '&No',
on_click: ->(_) { app.close_window(window); on_no&.call })
yes.layout(window.bounds.x + w / 2 - 9, window.bounds.y + h - 2, 8, 1)
no.layout(window.bounds.x + w / 2 + 1, window.bounds.y + h - 2, 8, 1)
window.add(yes)
window.add(no)
window.reset_focus
app.open_window(window)
window
end
# Single-line text input dialog. Calls on_submit with the entered value.
def self.input(app, prompt:, title: ' Input ', value: '', width: 50, on_submit: nil)
w = width
h = 7
window = centred_window(app, width: w, height: h, title: title)
label = Widgets::Label.new(text: prompt, style_key: :window_text)
label.layout(window.bounds.x + 2, window.bounds.y + 1, w - 4, 1)
window.add(label)
input = Widgets::TextInput.new(value: value)
input.layout(window.bounds.x + 2, window.bounds.y + 3, w - 4, 1)
window.add(input)
ok = Widgets::Button.new(label: '&OK',
on_click: ->(_) { app.close_window(window); on_submit&.call(input.value) })
cancel = Widgets::Button.new(label: '&Cancel',
on_click: ->(_) { app.close_window(window) })
ok.layout(window.bounds.x + w / 2 - 9, window.bounds.y + h - 2, 8, 1)
cancel.layout(window.bounds.x + w / 2 + 1, window.bounds.y + h - 2, 10, 1)
window.add(ok)
window.add(cancel)
window.reset_focus
window.focus_manager.focus(input)
app.open_window(window)
window
end
def self.wrap(text, width)
words = text.to_s.split
lines = []
cur = +''
words.each do |w|
if cur.empty?
cur << w
elsif cur.length + 1 + w.length <= width
cur << ' ' << w
else
lines << cur.dup
cur = +w
end
end
lines << cur unless cur.empty?
lines
end
end
end