A rubbs
BBS::Applicationépítőkövei: aBBS::Widgetalaposztály, aBBS::Widgetsösszes widgetje (Label, Button, TextInput, TextArea, Checkbox, ListBox, ScrollView, és a rajzokhoz a Scene), aContainer, és egy teljes saját widget. A window-k a Window-k oldalon vannak, maga az application az Application oldalon, a színek és a framebuffer a Rajzolás és témák oldalon. Minden a rubbs 1.0.0 forrásával egyeztetve. Az 1.0-ból kikerültek azok a widgetek, amelyeket egy board sem használt: aProgressBar, aSpinner, aHBox, aVBox, aPanelés azAnsiArt(a rajz mostantólBBS::ArtésWidgets::Scene, lásd Rajzok és grafikus képernyők); a widgeteket egyBBS::Window-ban kell elrendezni.
A widget egy Ruby-objektum, amely tudja, hol a helye a képernyőn, kirajzolja magát egy BBS::FrameBuffer-be, és ha fókuszálható, válaszol a billentyű- és egéreseményekre. Az application minden esemény után egy friss framebufferbe rajzolja újra az egész képernyőt, és csak a megváltozott cellákat küldi ki (lásd Rajzolás és témák), így a widget soha nem ír közvetlenül a vonalra: csak a saját állapotát módosítja, és kérésre kirajzolja azt.
Ami minden widgetre igaz:
layout(x, y, width, height) képernyőoszlopot és -sort vár, az (1, 1) a terminál bal felső sarka. A window-n belüli widgetet is képernyő-koordinátákkal helyezed el, nem a window-hoz képest. Ha a window elmozdul, az összes leszármazottját magával viszi.layout-ot, jellemzően a window saját pozíciójából vagy az application cols és rows értékéből számolva.:button_focused-ot, a téma pedig ANSI SGR-szekvenciává alakítja (lásd Rajzolás és témák).BBS::FocusManager kezeli window-nként (és egy az asztalon): a Tab és a Shift-Tab a hozzáadás sorrendjében lépteti a fókuszálható, látható és engedélyezett widgeteket, és billentyűt csak a fókuszban lévő widget kap.handle_event ítéletet ad vissza: a :handled megállítja az eseményt, a :halt kilép az applicationből, a nil (vagy false) továbbengedi a következő kezelőhöz.A BBS::Widget (lib/bbs/widget.rb) minden megjelenő elem alaposztálya. Önmagában semmit nem rajzol, és fókuszt sem kap.
BBS::Widget.new(id: nil, bounds: nil, theme: nil)
| Opció | Alapérték | Jelentés |
|---|---|---|
id: |
nil |
Bármilyen érték, többnyire Symbol, hogy a Container#find(id) később megtalálja a widgetet. |
bounds: |
Rect.new(1, 1, 0, 0) |
A kezdeti téglalap. A nulla szélesség azt jelenti, hogy nincs mit rajzolni; a legtöbb kód kihagyja, és inkább layout-ot hív. |
theme: |
nil |
Egy BBS::Theme. Általában kimarad: az application vagy a window állítja be, amikor a widget bekerül. |
| Attribútum | Hozzáférés | Alapérték | Jelentés |
|---|---|---|---|
bounds |
olvasható/írható | lásd fent | Egy BBS::Widget::Rect x, y, width, height mezőkkel. |
parent |
olvasható/írható | nil |
A befoglaló Container; a Container#add állítja be. |
id |
olvasható/írható | nil |
A Container#find keresési kulcsa. |
visible |
olvasható/írható | true |
A láthatatlan widget nem rajzolódik ki, és nem is fókuszálható. |
focused |
olvasható/írható | false |
A fókuszkezelő állítja. A widgetek ebből tudják, hogy a fókuszált stílust kell-e használniuk. |
enabled |
olvasható/írható | true |
A letiltott widgetet a Tab átugorja, nem fogad inputot, a gomb pedig szürkén rajzolódik. |
theme |
olvasható (theme= az írás) |
nil |
A widget saját témája; lásd lent a „Téma keresése” részt. |
A BBS::Widget::Rect egy Struct, contains?(px, py) metódussal (igaz, ha a pont belül van) és egy to_s-sel, például #2,2 8x1.
| Metódus | Mit csinál |
|---|---|
layout(x, y, width, height) |
Beállítja a bounds-ot. Az a widget, amely a saját részeit rendezi el, felülírhatja. |
render(frame) |
Kirajzolja a widgetet egy BBS::FrameBuffer-be. Az alaposztályban üres. |
handle_event(event) |
Válaszol egy BBS::Event-re. Az alaposztályban nil-t ad. |
focusable? |
Az alaposztályban false; az interaktív widgetek true-t adnak. |
on_focus / on_blur |
A fókuszkezelő hívja, amikor a widget megkapja vagy elveszti a fókuszt. Alapból üres. |
theme=(theme) |
Beállítja a témát. Container-en minden gyereknek továbbadja. |
hit_test(x, y) |
Saját magát adja vissza, ha a pont a bounds-on belül van, különben nil-t. A konténerek a legmélyebb gyereket adják. |
each { ... } |
Saját magát adja át a blokknak (a konténerek a walk-kal járják be a fájukat). |
redraw! |
Továbbadja a kérést a szülőnek. Az application úgyis újrarajzol minden esemény után, így ritkán kell. |
style(key) |
Protected: egy témaszerep SGR-stringje, a render-ben használatos. |
A style(key) a widget saját témáját használja, vagy ha az nil, a szülőjéét. Csak egy szintet néz fel. Az application abban a pillanatban adja oda a témát az egész fának, amikor átadod neki: az asztalon az add(widget) és az open_window(window) is theme=-t hív, a Container#theme= pedig minden akkor létező gyereknek továbbadja. Ezért előbb építsd fel az egész fát, és csak utána add hozzá. Ha a window megnyitása után kerül be egy widget két szint mélyre, nem kap stílust (a terminál alapszíneivel jelenik meg), hacsak magad nem hívod meg rajta a widget.theme = app.theme-et.
require 'bbs'
win = BBS::Window.new
win.theme = BBS::Theme.synchronet
box = BBS::Container.new
win.add(box) # a box-nak nincs saját témája, a szülőjének van
label = BBS::Widgets::Label.new(text: 'late')
box.add(label) # két szint mélyen, a theme= után
p label.send(:style, :text) # => nil, a terminál alapszíneivel rajzolódik
label.theme = win.theme # a megoldás
p label.send(:style, :text) # => "\e[0;37;40m"
A handle_event egy BBS::Event-et kap:
| Mező / metódus | Jelentés |
|---|---|
kind |
A widgetekhez :key vagy :mouse jut el. |
key |
A nevesített billentyűk Symbolok (:up, :down, :left, :right, :home, :end, :page_up, :page_down, :enter, :backspace, :delete, :tab, :shift_tab, :escape, :f1… , :alt_x, :shift_up, :ctrl_left…), a nyomtatható billentyű egykarakteres String. |
key?, mouse? |
A fajta. |
char? |
Igaz, ha a billentyű egykarakteres String. |
alt?, alt_char |
Igaz egy :alt_x billentyűre, és a betűje ('x'). |
mouse |
Egy BBS::Telnet::MouseEvent: kind (:press, :release, :move, :wheel), button (:left, :middle, :right, görgőnél :up/:down), x, y és mods. |
x, y |
Az egér pozíciója, 1-től számozott képernyő-koordinátákban. |
Tesztben kézzel is gyárthatsz eseményt: BBS::Event.key(:down), BBS::Event.key('a'), BBS::Event.mouse(BBS::Telnet::MouseEvent.new(kind: :press, button: :left, x: 3, y: 1)).
A widgetek többsége egy BBS::Window-ban él. Létrehozod a window-t, elhelyezed, hozzáadod a widgeteket a képernyőpozíciójukkal, meghívod a reset_focus-t, hogy a window fókuszkezelője lássa őket, és megnyitod. Erre jó hely egy helper metódus az applicationön. A kész window-típusok (Info, Form, MasterDetail, Stream) maguk építik fel a widgetjeiket; lásd Window-k.
require 'bbs'
APP = BBS::Application.define do
theme BBS::Theme.synchronet
status_hint 'Esc', 'Close'
helpers do
def settings_window
win = BBS::Window.new(title: ' Settings ')
win.layout(5, 3, 40, 11)
label = BBS::Widgets::Label.new(text: 'Handle:', style_key: :input_label)
label.layout(7, 5, 9, 1)
win.add(label)
handle = BBS::Widgets::TextInput.new(id: :handle, placeholder: 'Your handle', max_length: 20)
handle.layout(17, 5, 24, 1)
win.add(handle)
ansi = BBS::Widgets::Checkbox.new(id: :ansi, label: 'ANSI colours', checked: true)
ansi.layout(7, 7, 30, 1)
win.add(ansi)
themes = BBS::Widgets::ListBox.new(id: :theme, items: %i[synchronet nortoncommander mono])
themes.layout(7, 9, 20, 3)
win.add(themes)
save = BBS::Widgets::Button.new(label: '&Save', on_click: lambda { |_btn|
context[:handle] = win.find(:handle).value
close_window(win)
})
save.layout(30, 11, 12, 1)
win.add(save)
win.reset_focus
win
end
end
init do
title = BBS::Widgets::Label.new(text: 'My BBS', align: :center, style_key: :accent)
title.layout(1, 1, cols, 1)
add(title)
open_window(settings_window)
end
end
Fejetlenül, 50×15-ös képernyőn futtatva, a tasi begépelése, Tab és Szóköz (ez kiveszi a pipát) után így néz ki. A keret élénkfehér kéken, az aktív window címe fekete világosszürkén, a Handle: cián, a beviteli mező és a lista fehér kéken, a gomb fekete világosszürkén, piros S-sel:
My BBS
╔═[■]═ Settings ═══════════════════1══╗
║ ║
║ Handle: tasi ║
║ ║
║ [ ] ANSI colours ║
║ ║
║ synchronet ║
║ nortoncommander ║
║ mono [ Save ] ║
║ ║
╚══════════════════════════════════════╝
Esc Close
Az Alt-S (vagy az S, ha a fókuszban lévő widget nem fogad betűt) megnyomja a gombot, a context[:handle]-be teszi a tasi-t, és bezárja a window-t.
Ha egy callback be akarja zárni a window-t, maga hívja a close_window(win)-t. A Button on_click-jének eredményéből :handled (vagy :halt) lesz, így ha :close_window-t ad vissza, annak nincs hatása.
Az application init blokkjában (vagy bármely application-metódusból) add(widget)-del hozzáadott widgetek az asztalra kerülnek, a window-k mögötti területre. Ide tesz egy board üdvözlő artot, órát vagy egy nagy „főmenü” gombot:
require 'bbs'
APP = BBS::Application.define do
theme BBS::Theme.neumanntronics
init do
art = BBS::Widgets::Scene.new(art: BBS::Art.load('art/desktop.yml'), align: :center, valign: :middle)
art.layout(1, 2, cols, rows - 2)
add(art)
open_menu = BBS::Widgets::Button.new(label: '&Menu', on_click: ->(_) { open_window(:main) })
open_menu.layout(cols - 12, rows - 2, 10, 1)
add(open_menu)
end
end
Az asztal úgy működik, mint egy keret nélküli window, három eltéréssel (a forrással ellenőrizve):
&M) csak window-n belül működik.A window ebben a sorrendben kezel egy billentyűt (BBS::Window#handle_event):
close_key-e (alapból Esc) bezárja a window-t.A bal kattintás a kurzor alatti widgetre teszi a fókuszt, aztán az esemény a fókuszban lévő widgethez megy. A görgőesemény a kurzor alatti widgethez jut akkor is, ha nincs rajta fókusz, így egy ScrollView vagy ListBox kattintás nélkül is görgethető.
Mind a BBS::Widgets modulban vannak, mindegyik a saját fájljában a lib/bbs/widgets/ mappában. Minden konstruktor elfogadja a BBS::Widget-től örökölt id:, bounds: és theme: opciót is.
Egy sor szöveg, a szélességre vágva. Nem fókuszálható.
BBS::Widgets::Label.new(text: '', align: :left, style_key: :text)
| Opció | Alapérték | Jelentés |
|---|---|---|
text: |
'' |
A szöveg. Bármi, aminek van to_s-e. |
align: |
:left |
:left, :center vagy :right a szélességen belül. |
style_key: |
:text |
A témaszerep, amivel rajzolódik, pl. :accent, :label, :error, :window_text, :input_label. |
Mindhárom olvasható/írható attribútum, így a label helyben frissíthető (status.text = 'Saved'). A szöveg write_ansi-val íródik ki, tehát a beágyazott SGR-szekvenciák kiszínezhetik egy részét; a vágás és az igazítás viszont látható karakternek számolja az escape-karaktereket, így egy jobbra vagy középre igazított, escape-eket tartalmazó label túl balra kerül. A színezett labelt hagyd balra igazítva, vagy használj két labelt.
require 'bbs'
l1 = BBS::Widgets::Label.new(text: 'left')
l2 = BBS::Widgets::Label.new(text: 'center', align: :center)
l3 = BBS::Widgets::Label.new(text: 'right', align: :right, style_key: :accent)
[l1, l2, l3].each_with_index { |l, i| l.layout(1, i + 1, 20, 1) }
left
center
right
Kattintható gomb, [ Label ] alakban, a szélességében középre téve, kitöltött háttérrel.
BBS::Widgets::Button.new(label: 'OK', on_click: nil)
| Opció | Alapérték | Jelentés |
|---|---|---|
label: |
'OK' |
A felirat. Az & jelöli a hotkey betűjét: a '&Save' [ Save ]-et rajzol, az S a :button_hotkey színében. |
on_click: |
nil |
->(button) { ... }. A :halt visszatérési érték kilép az applicationből; minden más kezeltnek számít. |
Attribútumok: label, on_click (olvasható/írható). Metódusok: hotkey (a hotkey kisbetűsen, vagy nil), display_label (a felirat & nélkül), fire (úgy hívja meg az on_click-et, mint egy gombnyomás; letiltott gombon nem csinál semmit).
Viselkedés:
TextInput elviszi a sima betűket, így amíg szövegmező van fókuszban, Alt plusz a betű kell.:button, :button_focused, :button_hotkey, :button_disabled (ha enabled = false; ilyenkor a hotkey nincs kiemelve).require 'bbs'
quit = BBS::Widgets::Button.new(label: '&Quit', on_click: ->(_) { :halt })
p quit.hotkey # => "q"
p quit.display_label # => "Quit"
p quit.handle_event(BBS::Event.key(:enter)) # => :halt
ok = BBS::Widgets::Button.new(label: 'OK', on_click: ->(b) { puts "clicked #{b.label}" })
ok.handle_event(BBS::Event.key(' ')) # kiírja: "clicked OK", visszaad: :handled
Egy fókuszban lévő &Save gomb egy letiltott mellett (12-12 oszlop):
[ Save ] [ Cancel ]
Egysoros szövegmező kurzorral, vízszintes görgetéssel, placeholderrel és opcionális jelszómaszkkal.
BBS::Widgets::TextInput.new(value: '', placeholder: '', max_length: 256,
on_submit: nil, on_change: nil, password: false)
| Opció | Alapérték | Jelentés |
|---|---|---|
value: |
'' |
A kezdő szöveg (másolatként). |
placeholder: |
'' |
Szürke tipp (:text_dim), amíg a mező üres és nincs fókuszban. |
max_length: |
256 |
E hossz fölött a leütések elnyelődnek. |
on_submit: |
nil |
->(value, input) { ... } Enterre. Az eredményét adja vissza (ha az üres, :handled-et). |
on_change: |
nil |
->(value, input) { ... } minden szerkesztés után (gépelés, Backspace, Delete). |
password: |
false |
A karakterek helyett *-ot rajzol. |
Attribútumok: value (olvasható; a value= lecseréli a szöveget, és a végére teszi a kurzort), placeholder, max_length, on_submit, on_change, password (olvasható/írható).
Billentyűk: Bal, Jobb, Home, End mozgatja a kurzort; Backspace és Delete szerkeszt; Enter beküld; minden nyomtatható karakter beszúródik. A számára haszontalan billentyűkre (Esc, funkcióbillentyűk, Alt-betűk, Fel/Le) nil-t ad, így azok a window-hoz kerülnek: az Esc szövegmezőből is bezárja a window-t, az Alt-S is megnyomja a &Save-et. Egérkezelése nincs azon túl, hogy kattintásra a window ráteszi a fókuszt. Stílusok: :input, :input_focused, a placeholderhez :text_dim. A kurzor _.
require 'bbs'
input = BBS::Widgets::TextInput.new(
placeholder: 'Say something',
on_change: ->(v, _) { puts "change #{v}" },
on_submit: lambda { |v, field|
puts "submit #{v}"
field.value = '' # üríti a következő sorhoz, a kurzor visszaáll 0-ra
:handled
}
)
input.layout(1, 1, 30, 1)
'hi'.each_char { |c| input.handle_event(BBS::Event.key(c)) } # change h, change hi
p input.handle_event(BBS::Event.key(:escape)) # => nil (továbbmegy a window-hoz)
input.handle_event(BBS::Event.key(:enter)) # submit hi
p input.value # => ""
Egy üres, fókusz nélküli mező placeholderrel, és egy fókuszban lévő jelszómező secret tartalommal:
Your name
******_
Többsoros szerkesztő.
BBS::Widgets::TextArea.new(value: '', max_length: 8_000, on_change: nil)
| Opció | Alapérték | Jelentés |
|---|---|---|
value: |
'' |
Kezdő szöveg; a sorokat "\n" választja el. |
max_length: |
8_000 |
Az összes karakter (sortörésekkel együtt); itt megáll a gépelés. Az Enter ezután is tör sort. |
on_change: |
nil |
->(value, area) { ... } gépelés, Enter vagy Backspace után. |
Metódusok: value (a sorok "\n"-nel összefűzve), length. Az on_change olvasható/írható; value= nincs, a szöveg cseréjéhez új TextArea kell.
Billentyűk: a nyilak mozgatnak (a sor elején a Bal az előző sor végére visz, a sor végén a Jobb a következő sorra), Home/End a soron belül, az Enter a kurzornál kettévágja a sort, a Backspace töröl vagy összevonja az előző sorral, a nyomtatható karakterek beszúródnak. A nézet függőlegesen görgetve követi a kurzort; a hosszú sorokat levágja, nem tördeli és oldalra sem görgeti. Delete billentyű nincs, és Tab karakter sem (a Tab a fókuszt lépteti). Stílusok: :input, :input_focused.
require 'bbs'
body = BBS::Widgets::TextArea.new(value: "one\ntwo")
body.layout(1, 1, 40, 6)
body.handle_event(BBS::Event.key(:end)) # a "one" vége
body.handle_event(BBS::Event.key(:enter)) # új üres sor utána
body.handle_event(BBS::Event.key('!'))
p body.value # => "one\n!\ntwo"
p body.length # => 9
Kapcsoló, [x] Label vagy [ ] Label alakban.
BBS::Widgets::Checkbox.new(label: '', checked: false, on_change: nil)
| Opció | Alapérték | Jelentés |
|---|---|---|
label: |
'' |
A doboz utáni szöveg. |
checked: |
false |
Kezdőállapot. |
on_change: |
nil |
->(checked, box) { ... } minden váltás után. |
Attribútumok: label, checked, on_change (olvasható/írható). Szóköz vagy Enter vált. Kattintásra (a window-n keresztül) fókuszt kap, de nem vált. Stílusok: :text, fókuszban :button_focused.
require 'bbs'
remember = BBS::Widgets::Checkbox.new(label: 'Remember me', checked: true,
on_change: ->(on, _) { puts "checked=#{on}" })
remember.handle_event(BBS::Event.key(' ')) # kiírja: checked=false
[x] Remember me
Görgethető lista kijelöléssel.
BBS::Widgets::ListBox.new(items: [], on_select: nil, on_activate: nil, label: nil)
| Opció | Alapérték | Jelentés |
|---|---|---|
items: |
[] |
Bármilyen objektumok. |
label: |
nil |
->(item) { ... }, ami egy sor szövegét adja; nil esetén item.to_s. |
on_select: |
nil |
->(item, list) { ... }, ha a kijelölés másik sorra kerül (billentyű, kattintás, görgő). Nem hívódik, ha helyben marad. |
on_activate: |
nil |
->(item, list) { ... } Enterre. Az eredményét adja vissza (ha az üres, :handled-et). |
Attribútumok (olvasható/írható): items, selected (index, 0-ról indul), scroll (a legfelső látható sor indexe), on_select, on_activate, label. Metódus: selected_item.
Billentyűk: Fel, Le, PgUp és PgDn (a magasságnyit), Home, End mozgatja a kijelölést, és be is görgeti; Enter aktivál. Más billentyűre nil-t ad. Egér: a bal kattintás kijelöli az alatta lévő sort (nem aktivál, dupla kattintás nincs); a görgő három sorral lépteti a kijelölést. Stílusok: a sorokra :listbox, a kijelölt sorra :listbox_selected, ha a lista nincs fókuszban, és :listbox_focused, ha igen.
Ha lecseréled az items-et, a selected-et és a scroll-t magadnak kell visszaállítanod; a selected közvetlen beállítása nem görgeti be a sort. Üres listán az Enter nil-lel hívja az on_activate-et.
require 'bbs'
list = BBS::Widgets::ListBox.new(
items: %w[alpha beta gamma delta],
label: ->(name) { name.upcase },
on_select: ->(item, _) { puts "selected #{item}" },
on_activate: ->(item, _) { puts "activated #{item}"; :handled }
)
list.layout(1, 1, 20, 2)
list.handle_event(BBS::Event.key(:down)) # selected beta
list.handle_event(BBS::Event.key(:end)) # selected delta
p list.scroll # => 2, a nézet követte
list.handle_event(BBS::Event.key(:enter)) # activated delta
wheel = BBS::Telnet::MouseEvent.new(kind: :wheel, button: :up, x: 3, y: 1)
list.handle_event(BBS::Event.mouse(wheel)) # selected alpha
Ugyanez a lista három sor magasan, beta kijelölve (a sor teljes szélességében a kijelölés színével rajzolódik):
ALPHA
BETA
GAMMA
Egy tipikus window-beli főlista Enterre megnyitja a részleteket:
threads = BBS::Widgets::ListBox.new(
items: services[:boards].threads(:general),
label: ->(t) { "#{t.subject.ljust(30)} #{t.author}" },
on_activate: ->(t, _) { open_window(:thread, thread: t) }
)
Csak olvasható, görgethető sorblokk görgetősávval. Ez jeleníti meg a Markdownt és a hosszú szövegeket.
BBS::Widgets::ScrollView.new(lines: [])
| Opció | Alapérték | Jelentés |
|---|---|---|
lines: |
[] |
Stringek tömbje, soronként egy. A beágyazott ANSI SGR-szekvenciák megmaradnak; minden sor a szélességre vágódik. |
Attribútumok (olvasható/írható): lines, scroll (a legfelső látható sor indexe). Fókuszálható.
Billentyűk: Fel, Le, PgUp, PgDn, Home, End. Görgő: kattanásonként három sor (window-n belül fókusz nélkül is működik). Ha több a sor, mint a magasság, az utolsó oszlop görgetősáv lesz: │ a :scrollbar, █ csúszka a :scrollbar_thumb színében. A szöveg alapstílusa :text.
require 'bbs'
text = File.read('NEWS.md')
lines = BBS::Markdown.to_lines(text, width: 59, theme: BBS::Theme.synchronet)
news = BBS::Widgets::ScrollView.new(lines: lines)
news.layout(11, 5, 60, 15) # egy oszloppal szélesebb a szövegnél: hely a görgetősávnak
Tíz sor egy három sor magas nézetben:
line 1 █
line 2 │
line 3 │
A BBS::Container (lib/bbs/container.rb) gyerekeket tartó widget. A BBS::Window ennek alosztálya, és egy board ebben rendezi el a widgetjeit. Önmagában nem rendez el semmit: a layout csak a saját bounds-át állítja.
BBS::Container.new(id: nil, bounds: nil, theme: nil)
| Metódus | Mit csinál |
|---|---|
children |
A gyerekek tömbje, hozzáadási sorrendben (ez a rajzolási és a Tab-sorrend is). |
add(child) |
Hozzáfűzi a gyereket, beállítja a parent-jét, és visszaadja. |
remove(child) |
Kiveszi a gyereket, és törli a parent-jét. |
clear |
Minden gyereket kivesz. |
walk { ... } |
Átadja önmagát és minden leszármazottját, mélységi bejárással. |
find(id) |
Az első widget walk-sorrendben, amelyiknek egyezik az id-je, vagy nil. |
focusable_descendants |
Minden látható, fókuszálható leszármazott, sorrendben. |
render(frame) |
Sorban kirajzolja a látható gyerekeket (a későbbiek kerülnek felülre). |
hit_test(x, y) |
A pont alatti legfelső látható leszármazott, vagy maga a konténer, kívül pedig nil. |
theme=(theme) |
Beállítja a témát magán és minden gyerekén. |
require 'bbs'
form = BBS::Container.new
form.add(BBS::Widgets::TextInput.new(id: :name))
form.add(BBS::Widgets::Label.new(text: 'not focusable'))
form.add(BBS::Widgets::Checkbox.new(id: :news, label: 'Newsletter'))
p form.find(:news).label # => "Newsletter"
p form.focusable_descendants.map(&:id) # => [:name, :news]
A BBS::Widgets::Scene (lib/bbs/widgets/scene.rb) egy BBS::Art rajzot (YAML-fájl ANSI és PETSCII arccal) mutat a modern arcon, és minden rajzoláskor élő adattal tölti ki a hotspotjait. Nem fókuszálható. A 0.x AnsiArt widgetjét váltja; a rajzok, a hotspotok, az évszakos rétegek és az art-preview a Rajzok és grafikus képernyők oldalon vannak.
require 'bbs'
house = BBS::Art.load('art/house.yml')
scene = BBS::Widgets::Scene.new(art: house, align: :center, valign: :middle, fill_parent: true)
scene.fill(:sign) { |spot| "\e[1;37m#{inside_count}".ljust(spot.width) }
add(scene)
| Opció | Alapérték | Jelentés |
|---|---|---|
art: |
— | A BBS::Art. |
fills: |
{} |
Hotspotnév → blokk, ugyanaz, mint a fill(name) { ... } hívás. |
align: / valign: |
:left / :top |
Hol áll a rajz a bounds-on belül: :left, :center, :right / :top, :middle, :bottom. |
crop: |
true |
A rajznál kisebb képernyőn a bounds-ra vágja; false esetén ilyenkor semmit sem rajzol (ezt a fits? mondja meg). |
fill_parent: |
false |
Minden rajzoláskor a szülő bounds-át veszi át (az asztalét, a menüsor és a státuszsor között). |
Származtass a BBS::Widget-ből (vagy a BBS::Container-ből, ha gyerekeket tart). A szerződés:
**kw) add tovább a super-nek, hogy az id:, bounds: és theme: továbbra is működjön.focusable? adjon true-t, ha inputot fogad.render(frame) csak a bounds-on belül rajzoljon, frame.move(bounds.x, bounds.y)-nal kezdve, a színeket pedig style(:role)-lal kérje. Meglévő témaszerepeket használj, hogy minden preset értelmesen színezze a widgetet.handle_event(event) adjon :handled-et arra, amit felhasznált, és nil-t minden másra, hogy a Tab, az Esc és a hotkeyek tovább működjenek.Egy csillagos értékelő widget, teljes egészében:
require 'bbs'
class Rating < BBS::Widget
attr_reader :value
attr_accessor :on_change
def initialize(value: 0, max: 5, on_change: nil, **kw)
super(**kw)
@value = value
@max = max
@on_change = on_change
end
def focusable? = true
def value=(v)
v = v.clamp(0, @max)
return if v == @value
@value = v
@on_change&.call(@value, self)
end
def render(frame)
return if bounds.width <= 0
stars = ('*' * @value).ljust(@max, '.')
frame.move(bounds.x, bounds.y)
frame.write("[#{stars}] #{@value}/#{@max}"[0, bounds.width],
sgr: style(focused ? :input_focused : :input))
end
def handle_event(event)
return nil unless enabled
if event.key?
case event.key
when :left, '-' then self.value = @value - 1
when :right, '+' then self.value = @value + 1
when '0'..'9' then self.value = event.key.to_i
else return nil
end
:handled
elsif event.mouse? && event.mouse.kind == :press && bounds.contains?(event.x, event.y)
self.value = event.x - bounds.x
:handled
end
end
end
Kipróbálva szerver nélkül, egyenesen egy framebufferbe:
r = Rating.new(value: 2, on_change: ->(v, _) { puts "rating #{v}" })
r.theme = BBS::Theme.synchronet
r.layout(1, 1, 20, 1)
r.handle_event(BBS::Event.key(:right)) # rating 3
r.handle_event(BBS::Event.key('9')) # rating 5 (korlátozva)
p r.handle_event(BBS::Event.key(:up)) # => nil, nem a miénk
click = BBS::Telnet::MouseEvent.new(kind: :press, button: :left, x: 3, y: 1)
r.handle_event(BBS::Event.mouse(click)) # rating 2: a második csillagra kattintott
fb = BBS::FrameBuffer.new(20, 1)
r.render(fb)
[**...] 2/5
Window-ban ugyanúgy használható, mint bármelyik beépített widget:
stars = Rating.new(id: :stars, value: 3)
stars.layout(win.bounds.x + 2, win.bounds.y + 2, 16, 1)
win.add(stars)
win.reset_focus
define, init, add, open_window, on_tick, with_lineBBS::Window, a kész window-típusok, dialógusokFrameBuffer, MarkdownBBS::Art, Widgets::Scene, évszakos rétegek