new framework
This commit is contained in:
88
lib/bbs/statusbar.rb
Normal file
88
lib/bbs/statusbar.rb
Normal file
@@ -0,0 +1,88 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require_relative 'widget'
|
||||
|
||||
module BBS
|
||||
# A status bar at the bottom of the screen. Render a list of hotkey hints
|
||||
# like "F1=Help F2=Save Esc=Cancel". Optionally a right-aligned status text.
|
||||
class Statusbar < Widget
|
||||
Hint = Struct.new(:key, :label, :on_activate, keyword_init: true)
|
||||
|
||||
attr_accessor :hints, :right_text
|
||||
|
||||
def initialize(hints: [], right_text: nil, **kw)
|
||||
super(**kw)
|
||||
@hints = hints
|
||||
@right_text = right_text
|
||||
end
|
||||
|
||||
def add_hint(key, label, &on_activate)
|
||||
@hints << Hint.new(key: key, label: label, on_activate: on_activate)
|
||||
end
|
||||
|
||||
def render(frame)
|
||||
sgr = style(:statusbar)
|
||||
key_sgr = style(:statusbar_key)
|
||||
frame.move(bounds.x, bounds.y)
|
||||
frame.write(' ' * bounds.width, sgr: sgr)
|
||||
|
||||
cx = bounds.x + 1
|
||||
@hints.each do |hint|
|
||||
text_key = hint.key.to_s
|
||||
text_lbl = " #{hint.label} "
|
||||
break if cx + text_key.length + text_lbl.length > bounds.x + bounds.width - 1
|
||||
frame.move(cx, bounds.y)
|
||||
frame.write(text_key, sgr: key_sgr)
|
||||
frame.write(text_lbl, sgr: sgr)
|
||||
cx += text_key.length + text_lbl.length + 1
|
||||
end
|
||||
|
||||
if @right_text
|
||||
txt = @right_text.to_s
|
||||
rx = bounds.x + bounds.width - txt.length - 1
|
||||
if rx >= cx
|
||||
frame.move(rx, bounds.y)
|
||||
frame.write(txt, sgr: sgr)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def handle_event(event)
|
||||
if event.key?
|
||||
hint = match_key(event.key)
|
||||
return hint.on_activate&.call || :handled if hint
|
||||
return nil
|
||||
end
|
||||
|
||||
return nil unless event.mouse? && event.mouse.kind == :press &&
|
||||
event.mouse.button == :left && event.y == bounds.y
|
||||
cx = bounds.x + 1
|
||||
@hints.each do |hint|
|
||||
text_key = hint.key.to_s
|
||||
text_lbl = " #{hint.label} "
|
||||
width = text_key.length + text_lbl.length
|
||||
if event.x >= cx && event.x < cx + width
|
||||
return hint.on_activate&.call || :handled
|
||||
end
|
||||
cx += width + 1
|
||||
end
|
||||
nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def match_key(key)
|
||||
key_str = key.to_s
|
||||
@hints.find do |hint|
|
||||
label = hint.key.to_s
|
||||
case label
|
||||
when /\AF(\d+)\z/i then key == :"f#{::Regexp.last_match(1)}"
|
||||
when /\AAlt-(.)\z/i then key == :"alt_#{::Regexp.last_match(1).downcase}"
|
||||
when /\AEsc\z/i then key == :escape
|
||||
when /\ATab\z/i then key == :tab
|
||||
else key_str.casecmp?(label)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user