├── LICENSE ├── META6.json ├── README.md ├── doc ├── Vikna.rakudoc ├── Vikna │ ├── Classes.rakudoc │ ├── Events.rakudoc │ ├── Manual.rakudoc │ ├── Test.rakudoc │ └── Widget.rakudoc └── image │ └── demo.gif ├── docs └── md │ ├── Vikna.md │ └── Vikna │ ├── CAttr.md │ ├── Canvas.md │ ├── Child.md │ ├── Classes.md │ ├── Color.md │ ├── Color │ ├── Index.md │ ├── Named.md │ ├── RGB.md │ └── RGBA.md │ ├── CommandHandling.md │ ├── Coord.md │ ├── EventHandling.md │ ├── Events.md │ ├── Manual.md │ ├── OS.md │ ├── Object.md │ ├── Parent.md │ ├── Point.md │ ├── Rect.md │ ├── Test.md │ ├── WAttr.md │ ├── Widget.md │ └── X.md ├── examples ├── button.raku ├── input-line.raku ├── mouse-basic.raku ├── threaded-rainbow-group.raku ├── threaded-rainbow.raku ├── tscroll.raku ├── window.raku └── z-order.raku ├── lib ├── Vikna.rakumod └── Vikna │ ├── App.rakumod │ ├── Border.rakumod │ ├── Button.rakumod │ ├── CAttr.rakumod │ ├── Canvas.rakumod │ ├── Child.rakumod │ ├── Color.rakumod │ ├── Color │ ├── Index.rakumod │ ├── Named.rakumod │ ├── RGB.rakumod │ └── RGBA.rakumod │ ├── CommandHandling.rakumod │ ├── Coord.rakumod │ ├── Desktop.rakumod │ ├── Dev │ ├── Kbd.rakumod │ └── Mouse.rakumod │ ├── Elevatable.rakumod │ ├── EventEmitter.rakumod │ ├── EventHandling.rakumod │ ├── Events.rakumod │ ├── Focusable.rakumod │ ├── InputLine.rakumod │ ├── Label.rakumod │ ├── META.rakumod │ ├── OS.rakumod │ ├── OS │ └── unix.rakumod │ ├── Object.rakumod │ ├── Parent.rakumod │ ├── Point.rakumod │ ├── PointerTarget.rakumod │ ├── Rect.rakumod │ ├── Screen.rakumod │ ├── Screen │ └── ANSI.rakumod │ ├── Scrollable.rakumod │ ├── Test.rakumod │ ├── Test │ ├── App.rakumod │ ├── OS.rakumod │ └── Screen.rakumod │ ├── TextScroll.rakumod │ ├── Tracer.rakumod │ ├── Utils.rakumod │ ├── WAttr.rakumod │ ├── Widget.rakumod │ ├── Widget │ ├── Group.rakumod │ └── GroupMember.rakumod │ ├── Window.rakumod │ └── X.rakumod ├── materials └── TPC2020 │ ├── sample-1.raku │ └── sample-2.raku ├── resources ├── color-index.json └── tracer │ ├── html.tmpl │ └── txt.tmpl ├── t ├── 009-parent-child.t ├── 010-rectangles.t ├── 011-events.t ├── 020-canvas.t ├── 030-tracer.t ├── 040-color.t ├── 050-style-bits.t ├── 060-test-app.t ├── 070-flows.t ├── desktop │ └── 010-screen-resize.t └── widget │ └── 010-basic.t └── tools └── tracer /META6.json: -------------------------------------------------------------------------------- 1 | { 2 | "auth": "github:vrurg", 3 | "authors": [ 4 | "Vadim Belman " 5 | ], 6 | "build-depends": [ 7 | ], 8 | "depends": [ 9 | "Template::Mustache", 10 | "DB::SQLite", 11 | "Terminal::Print", 12 | "AttrX::Mooish:ver<0.7.3+>", 13 | "Color", 14 | "Color::Names", 15 | "Cache::Async", 16 | "Concurrent::PChannel", 17 | "Hash::Merge" 18 | ], 19 | "description": "Console UI toolkit", 20 | "license": "Artistic-2.0", 21 | "meta-version": "0", 22 | "name": "Vikna", 23 | "perl": "6.e", 24 | "production": true, 25 | "provides": { 26 | "Vikna": "lib/Vikna.rakumod", 27 | "Vikna::App": "lib/Vikna/App.rakumod", 28 | "Vikna::Border": "lib/Vikna/Border.rakumod", 29 | "Vikna::Button": "lib/Vikna/Button.rakumod", 30 | "Vikna::CAttr": "lib/Vikna/CAttr.rakumod", 31 | "Vikna::Canvas": "lib/Vikna/Canvas.rakumod", 32 | "Vikna::Child": "lib/Vikna/Child.rakumod", 33 | "Vikna::Color": "lib/Vikna/Color.rakumod", 34 | "Vikna::Color::Index": "lib/Vikna/Color/Index.rakumod", 35 | "Vikna::Color::Named": "lib/Vikna/Color/Named.rakumod", 36 | "Vikna::Color::RGB": "lib/Vikna/Color/RGB.rakumod", 37 | "Vikna::Color::RGBA": "lib/Vikna/Color/RGBA.rakumod", 38 | "Vikna::CommandHandling": "lib/Vikna/CommandHandling.rakumod", 39 | "Vikna::Coord": "lib/Vikna/Coord.rakumod", 40 | "Vikna::Desktop": "lib/Vikna/Desktop.rakumod", 41 | "Vikna::Dev::Kbd": "lib/Vikna/Dev/Kbd.rakumod", 42 | "Vikna::Dev::Mouse": "lib/Vikna/Dev/Mouse.rakumod", 43 | "Vikna::Elevatable": "lib/Vikna/Elevatable.rakumod", 44 | "Vikna::EventEmitter": "lib/Vikna/EventEmitter.rakumod", 45 | "Vikna::EventHandling": "lib/Vikna/EventHandling.rakumod", 46 | "Vikna::Events": "lib/Vikna/Events.rakumod", 47 | "Vikna::Focusable": "lib/Vikna/Focusable.rakumod", 48 | "Vikna::InputLine": "lib/Vikna/InputLine.rakumod", 49 | "Vikna::Label": "lib/Vikna/Label.rakumod", 50 | "Vikna::OS": "lib/Vikna/OS.rakumod", 51 | "Vikna::OS::unix": "lib/Vikna/OS/unix.rakumod", 52 | "Vikna::Object": "lib/Vikna/Object.rakumod", 53 | "Vikna::Parent": "lib/Vikna/Parent.rakumod", 54 | "Vikna::Point": "lib/Vikna/Point.rakumod", 55 | "Vikna::PointerTarget": "lib/Vikna/PointerTarget.rakumod", 56 | "Vikna::Rect": "lib/Vikna/Rect.rakumod", 57 | "Vikna::Screen": "lib/Vikna/Screen.rakumod", 58 | "Vikna::Screen::ANSI": "lib/Vikna/Screen/ANSI.rakumod", 59 | "Vikna::Scrollable": "lib/Vikna/Scrollable.rakumod", 60 | "Vikna::Test": "lib/Vikna/Test.rakumod", 61 | "Vikna::Test::App": "lib/Vikna/Test/App.rakumod", 62 | "Vikna::Test::OS": "lib/Vikna/Test/OS.rakumod", 63 | "Vikna::Test::Screen": "lib/Vikna/Test/Screen.rakumod", 64 | "Vikna::TextScroll": "lib/Vikna/TextScroll.rakumod", 65 | "Vikna::Tracer": "lib/Vikna/Tracer.rakumod", 66 | "Vikna::Utils": "lib/Vikna/Utils.rakumod", 67 | "Vikna::WAttr": "lib/Vikna/WAttr.rakumod", 68 | "Vikna::Widget": "lib/Vikna/Widget.rakumod", 69 | "Vikna::Widget::Group": "lib/Vikna/Widget/Group.rakumod", 70 | "Vikna::Widget::GroupMember": "lib/Vikna/Widget/GroupMember.rakumod", 71 | "Vikna::Window": "lib/Vikna/Window.rakumod", 72 | "Vikna::X": "lib/Vikna/X.rakumod" 73 | }, 74 | "resources": [ 75 | "tracer/html.tmpl", 76 | "tracer/txt.tmpl", 77 | "color-index.json" 78 | ], 79 | "source-url": "https://github.com/vrurg/raku-Vikna.git", 80 | "support": { 81 | "source": "https://github.com/vrurg/raku-Vikna" 82 | }, 83 | "tags": [ 84 | "Vikna", 85 | "UI", 86 | "console" 87 | ], 88 | "test-depends": [ 89 | "Test", 90 | "Test::Async", 91 | "Test::META" 92 | ], 93 | "version": "0.0.3" 94 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Raku Console UI Toolkit Vikna 2 | 3 | Pronounced as _Veekna_. In Ukrainian it means 'windows'. ;) 4 | 5 | ![Demo](https://github.com/vrurg/raku-Vikna/blob/master/doc/image/demo.gif) 6 | 7 | # **IMPORTANT!** 8 | 9 | Vikna is in its deep, deep infancy stage. Mostly being a proof of concept now than a real thing. Yet, it already has 10 | many of the key elements an UI framework must have. Just check out 11 | [_./examples_](https://github.com/vrurg/raku-Vikna/tree/v0.0.1/examples) directory to get the first impression. But be 12 | patient when trying [_threaded-rainbow.raku_](https://github.com/vrurg/raku-Vikna/blob/master/examples/threaded-rainbow.raku). 13 | 14 | The project requires Raku to implement chained dispatching. Because my implementation of the feature for 15 | Rakudo had to be reverted from the _master_ Vikna won't work correctly with standard Rakudo releases. But it can be tried with 16 | _v-dispatchers_ branch in either [main Rakudo repository](https://github.com/rakudo/rakudo/tree/v-dispatchers) or in 17 | [my fork of it](https://github.com/vrurg/rakudo/tree/v-dispatchers). The situation will change when Jonathan Worthington 18 | will succeed with his total redesign of dispatching which is much awaited by many as it gonna bring a lot of new 19 | opportunities to Rakudo. 20 | 21 | ## SEE ALSO 22 | 23 | [`Vikna`](https://github.com/vrurg/raku-Vikna/blob/master/docs/md/Vikna.md) 24 | 25 | -------------------------------------------------------------------------------- /doc/Vikna.rakudoc: -------------------------------------------------------------------------------- 1 | =begin pod 2 | =NAME 3 | C – all native event-driven Raku console UI 4 | 5 | =SYNOPSIS 6 | 7 | use Vikna::App; 8 | use Vikna::Window; 9 | use Vikna::Button; 10 | use Vikna::Event; 11 | 12 | class MyWin is Vikna::Window { 13 | multi method event(Event::Button::Click:D $ev) { 14 | $.desktop.quit; 15 | } 16 | } 17 | 18 | class MyApp is Vikna::App { 19 | method main { 20 | my $w = $.desktop.create-child: Vikna::Window, 21 | :x(5), :y(5), :w(20), :h(10), 22 | :name('MainWin'), :title('Main Window'); 23 | $w.create-child: Vikna::Button, :x(1), :y(1), :text("Quit"), :target($w); 24 | } 25 | } 26 | 27 | MyApp.run; 28 | 29 | =DESCRIPTION 30 | 31 | This framework is an attempt to provide a full-fledged console UI for L. It aims at few primary 32 | targets: 33 | 34 | =item Be a multi-platform. This is achieved by: 35 | =item2 being a pure Raku, i.e. avoid use of any native libraries 36 | =item2 being OS-independent by incapsulating any OS-specific logic in a driver-like layer 37 | =item Support fully asynchronous model of development 38 | 39 | Any other implementation specifics of the framework are decisions taken to meet the above targets. 40 | 41 | More information can be found in the following sections: 42 | 43 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md> 44 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Classes.md> 45 | 46 | =DISCLAIMER 47 | 48 | The project is currently in the early development stage serving more as a proof of concept than anything else. For 49 | anyone willing to try it it means bugs and slowness in many use cases. Also, apparently the API is far from being stable 50 | and can change any time, perhaps even without a prior warning. Things are gonna be this way up until v0.1.0 release 51 | I expect the API to stabilize up to the level to be promised that nothing gets changed without a few weeks deprecation 52 | period. v0.2.0 should bring in even more strict policies, yet this future is too far now to be discussed. 53 | 54 | =AUTHOR 55 | 56 | Vadim Belman 57 | 58 | =end pod -------------------------------------------------------------------------------- /doc/Vikna/Classes.rakudoc: -------------------------------------------------------------------------------- 1 | =begin pod 2 | =head1 Vikna CLASSES AND ROLES 3 | 4 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/App.md> 5 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Border.md> 6 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Button.md> 7 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Canvas.md> 8 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/CAttr.md> 9 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Child.md> 10 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color.md> 11 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color/Index.md> 12 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color/Named.md> 13 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color/RGB.md> 14 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color/RGBA.md> 15 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/CommandHandling.md> 16 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Coord.md> 17 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Desktop.md> 18 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Elevatable.md> 19 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/EventEmitter.md> 20 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/EventHandling.md> 21 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Events.md> 22 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Focusable.md> 23 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/InputLine.md> 24 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Label.md> 25 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Object.md> 26 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/OS.md> 27 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/OS/unix.md> 28 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Parent.md> 29 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Point.md> 30 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/PointerTarget.md> 31 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Rect.md> 32 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Screen.md> 33 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Screen/ANSI.md> 34 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Scrollable.md> 35 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Test.md> 36 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Test/App.md> 37 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Test/OS.md> 38 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Test/Screen.md> 39 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/TextScroll.md> 40 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Tracer.md> 41 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/WAttr.md> 42 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Widget.md> 43 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Widget/Group.md> 44 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Widget/GroupMember.md> 45 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Window.md> 46 | =item L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/X.md> 47 | 48 | =AUTHOR 49 | 50 | Vadim Belman 51 | 52 | =end pod -------------------------------------------------------------------------------- /doc/image/demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vrurg/raku-Vikna/9604ecc89320f1bd0eee539095c6cfa6f6d7bbe8/doc/image/demo.gif -------------------------------------------------------------------------------- /docs/md/Vikna.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | `Vikna` – all native event-driven Raku console UI 5 | 6 | SYNOPSIS 7 | ======== 8 | 9 | 10 | 11 | use Vikna::App; 12 | use Vikna::Window; 13 | use Vikna::Button; 14 | use Vikna::Events; 15 | 16 | class MyWin is Vikna::Window { 17 | multi method event(Event::Mouse::Click:D $ev) { 18 | $.desktop.quit; 19 | } 20 | } 21 | 22 | class MyApp is Vikna::App { 23 | method main { 24 | my $w = $.desktop.create-child: Vikna::Window, 25 | :x(5), :y(5), :w(20), :h(10), 26 | :name('MainWin'), :title('Main Window'); 27 | $w.create-child: Vikna::Button, :x(1), :y(1), :text("Quit"), :target($w); 28 | } 29 | } 30 | 31 | MyApp.run; 32 | 33 | DESCRIPTION 34 | =========== 35 | 36 | 37 | 38 | This framework is an attempt to provide a full-fledged console UI for [Raku](https://raku.org). It aims at few primary targets: 39 | 40 | * Be a multi-platform. This is achieved by: 41 | 42 | * being a pure Raku, i.e. avoid use of any native libraries 43 | 44 | * being OS-independent by incapsulating any OS-specific logic in a driver-like layer 45 | 46 | * Support fully asynchronous model of development 47 | 48 | Any other implementation specifics of the framework are decisions taken to meet the above targets. 49 | 50 | More information can be found in the following sections: 51 | 52 | * [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md) 53 | 54 | * [`Vikna::Classes`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Classes.md) 55 | 56 | DISCLAIMER 57 | ========== 58 | 59 | 60 | 61 | The project is currently in the early development stage serving more as a proof of concept than anything else. For anyone willing to try it it means bugs and slowness in many use cases. Also, apparently the API is far from being stable and can change any time, perhaps even without a prior warning. Things are gonna be this way up until v0.1.0 release I expect the API to stabilize up to the level to be promised that nothing gets changed without a few weeks deprecation period. v0.2.0 should bring in even more strict policies, yet this future is too far now to be discussed. 62 | 63 | AUTHOR 64 | ====== 65 | 66 | 67 | 68 | Vadim Belman 69 | 70 | -------------------------------------------------------------------------------- /docs/md/Vikna/CAttr.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | 5 | 6 | `Vikna::CAttr` - on screen symbol attributes 7 | 8 | DESCRIPTION 9 | =========== 10 | 11 | 12 | 13 | Class defines basic attributes of a symbol on screen: it's foreground and background colors, and style. 14 | 15 | ATTRIBUTES 16 | ========== 17 | 18 | 19 | 20 | ### `$.fg` 21 | 22 | Foreground color 23 | 24 | ### `$.bg` 25 | 26 | Bacground color 27 | 28 | ### `Int $.style` 29 | 30 | Style of the symbol. See `VS*` constants in [`Vikna::Utils`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Utils.md). 31 | 32 | ### `%.Profile` 33 | 34 | Cached representation of the attribute suitable for passing into methods like [`Vikna::Canvas`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Canvas.md)`::imprint`. 35 | 36 | METHODS 37 | ======= 38 | 39 | 40 | 41 | ### `new(*%c)` 42 | 43 | ### `clone(*%c)` 44 | 45 | ### `dup(*%c)` 46 | 47 | All three methods preserve their usual meaning with one nuance: if `style` key is passed in `%c` profile then it gets normalized using `to-style` routine from [`Vikna::Utils`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Utils.md). 48 | 49 | ### `bold()`, `italic()`, `underline()` 50 | 51 | Methods return *True* if corresponding style is set. 52 | 53 | ### `transparent()` 54 | 55 | Returns *True* style is transparent. 56 | 57 | ### `style-char()` 58 | 59 | Returns style representation as a single char. For example, for non-transparent style if nothing is set it would be just space character *" "* (code *0x20* which is the value of `VSNone` constant). For bold which is represented as `VSBase +| VSBold` it will be exclamation mark *"!"* (code *0x21*). 60 | 61 | ### `styles()` 62 | 63 | Returns a list of style `VS*` constants. 64 | 65 | ROUTINES 66 | ======== 67 | 68 | 69 | 70 | ### `multi sub cattr($fg, $bg?, $style?)` 71 | 72 | ### `multi sub cattr(:$fg, :$bg, :$style)` 73 | 74 | A shortcut to create a new `Vikna::CAttr` instance. 75 | 76 | SEE ALSO 77 | ======== 78 | 79 | [`Vikna`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna.md), [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md), 80 | 81 | AUTHOR 82 | ====== 83 | 84 | Vadim Belman 85 | 86 | -------------------------------------------------------------------------------- /docs/md/Vikna/Child.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | 5 | 6 | `Vikna::Child` - child rol 7 | 8 | DESCRIPTION 9 | =========== 10 | 11 | 12 | 13 | Very simplistic role only defining `$.parent` attribute, `set-parent` and `has-parent` methods. 14 | 15 | Requires `id` method from the consuming class. 16 | 17 | SEE ALSO 18 | ======== 19 | 20 | [`Vikna`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna.md), [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md) 21 | 22 | AUTHOR 23 | ====== 24 | 25 | 26 | 27 | Vadim Belman 28 | 29 | -------------------------------------------------------------------------------- /docs/md/Vikna/Classes.md: -------------------------------------------------------------------------------- 1 | Vikna CLASSES AND ROLES 2 | ======================= 3 | 4 | * [`Vikna::App`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/App.md) 5 | 6 | * [`Vikna::Border`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Border.md) 7 | 8 | * [`Vikna::Button`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Button.md) 9 | 10 | * [`Vikna::Canvas`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Canvas.md) 11 | 12 | * [`Vikna::CAttr`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/CAttr.md) 13 | 14 | * [`Vikna::Child`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Child.md) 15 | 16 | * [`Vikna::Color`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color.md) 17 | 18 | * [`Vikna::Color::Index`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color/Index.md) 19 | 20 | * [`Vikna::Color::Named`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color/Named.md) 21 | 22 | * [`Vikna::Color::RGB`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color/RGB.md) 23 | 24 | * [`Vikna::Color::RGBA`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color/RGBA.md) 25 | 26 | * [`Vikna::CommandHandling`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/CommandHandling.md) 27 | 28 | * [`Vikna::Coord`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Coord.md) 29 | 30 | * [`Vikna::Desktop`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Desktop.md) 31 | 32 | * [`Vikna::Elevatable`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Elevatable.md) 33 | 34 | * [`Vikna::EventEmitter`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/EventEmitter.md) 35 | 36 | * [`Vikna::EventHandling`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/EventHandling.md) 37 | 38 | * [`Vikna::Events`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Events.md) 39 | 40 | * [`Vikna::Focusable`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Focusable.md) 41 | 42 | * [`Vikna::InputLine`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/InputLine.md) 43 | 44 | * [`Vikna::Label`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Label.md) 45 | 46 | * [`Vikna::Object`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Object.md) 47 | 48 | * [`Vikna::OS`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/OS.md) 49 | 50 | * [`Vikna::OS::unix`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/OS/unix.md) 51 | 52 | * [`Vikna::Parent`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Parent.md) 53 | 54 | * [`Vikna::Point`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Point.md) 55 | 56 | * [`Vikna::PointerTarget`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/PointerTarget.md) 57 | 58 | * [`Vikna::Rect`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Rect.md) 59 | 60 | * [`Vikna::Screen`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Screen.md) 61 | 62 | * [`Vikna::Screen::ANSI`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Screen/ANSI.md) 63 | 64 | * [`Vikna::Scrollable`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Scrollable.md) 65 | 66 | * [`Vikna::Test`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Test.md) 67 | 68 | * [`Vikna::Test::App`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Test/App.md) 69 | 70 | * [`Vikna::Test::OS`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Test/OS.md) 71 | 72 | * [`Vikna::Test::Screen`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Test/Screen.md) 73 | 74 | * [`Vikna::TextScroll`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/TextScroll.md) 75 | 76 | * [`Vikna::Tracer`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Tracer.md) 77 | 78 | * [`Vikna::WAttr`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/WAttr.md) 79 | 80 | * [`Vikna::Widget`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Widget.md) 81 | 82 | * [`Vikna::Widget::Group`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Widget/Group.md) 83 | 84 | * [`Vikna::Widget::GroupMember`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Widget/GroupMember.md) 85 | 86 | * [`Vikna::Window`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Window.md) 87 | 88 | * [`Vikna::X`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/X.md) 89 | 90 | AUTHOR 91 | ====== 92 | 93 | 94 | 95 | Vadim Belman 96 | 97 | -------------------------------------------------------------------------------- /docs/md/Vikna/Color.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | 5 | 6 | `Vikna::Color` - support for different formats of colors 7 | 8 | SYNOPSIS 9 | ======== 10 | 11 | 12 | 13 | my $color = Vikna::Color.parse: '#abc'; # RGB 0xAA, 0xBB, 0xCC 14 | $color = Vikna::Color.parse: '42, 255, 13'; 15 | $color = Vikna::Color.parse: 'rgba: .1, .2, .3, .5'; 16 | 17 | DESCRIPTION 18 | =========== 19 | 20 | 21 | 22 | Inherits from [`Color`](https://modules.raku.org/dist/Color). 23 | 24 | This class function is to provide interface for working with string representation of colors. It supports colors in the following forms: 25 | 26 | * ANSI index: *123* 27 | 28 | * web: *#00aa80*, *#abc* 29 | 30 | * named: *green* 31 | 32 | * RGB triplet: *255,0,128* 33 | 34 | * RGB decimal triplet: *1, 0.1, .5* 35 | 36 | * prefixed form: *rgba: 1, 0.5, 0.9, 0.3* 37 | 38 | For prefixed form knwon prefixes are *rgb*, *rgbd*, *rgba*, *rgbad*, *cmyk*, *hsl*, *hsla*, *hsv*, *hsva* - following the key names supported by [`Color`](https://modules.raku.org/dist/Color) class. 39 | 40 | The only method to mention is `parse` which takes a string a returns either a `Vikna::Color` instance or a Nil if the color string is invalid. To be more precise, the object returned will have with one of `Vikna::Color` roles mixed in: [`Vikna::Color::Index`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color/Index.md), [`Vikna::Color::Named`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color/Named.md), [`Vikna::Color::RGB`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color/RGB.md), [`Vikna::Color::RGBA`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color/RGBA.md). The difference between the four is in the way they strigify by default and additional methods provided depending on the format. 41 | 42 | Apparently, API provided by [`Coloar`](https://modules.raku.org/dist/Coloar) is available too. 43 | 44 | Caching 45 | ------- 46 | 47 | Color objects are cached internally to speed up color lookups. But it also means that same color object could be returned for two equivalent color strings. Nevertheless, the equivalence of the objects is not guaranteed due to limited cache size. 48 | 49 | SEE ALSO 50 | ======== 51 | 52 | [`Vikna`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna.md), [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md), [`Color`](https://modules.raku.org/dist/Color), [`Color::Names`](https://modules.raku.org/dist/Color::Names), [`Vikna::Color::Index`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color/Index.md), [`Vikna::Color::Named`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color/Named.md), [`Vikna::Color::RGB`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color/RGB.md), [`Vikna::Color::RGBA`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color/RGBA.md) 53 | 54 | AUTHOR 55 | ====== 56 | 57 | 58 | 59 | Vadim Belman 60 | 61 | -------------------------------------------------------------------------------- /docs/md/Vikna/Color/Index.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | 5 | 6 | `Vikna::Color::Index` - a role representing indexed color 7 | 8 | ATTRIBUTES 9 | ========== 10 | 11 | 12 | 13 | ### `Int $.index` 14 | 15 | The index used to create the object. 16 | 17 | METHODS 18 | ======= 19 | 20 | 21 | 22 | ### `method rgb-by-index(Int:D $idx)` 23 | 24 | Takes an ANSI color index and returns its RGB representation as a [`Hash`](https://docs.raku.org/type/Hash) with keys `r`, `g`, `b`. If no such index exists then an empty hash is returned. 25 | 26 | *Note* that the representation is taken from file *resources/color-index.json*. 27 | 28 | Stringifies into index. 29 | 30 | SEE ALSO 31 | ======== 32 | 33 | [`Vikna`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna.md), [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md), [`Vikna::Color`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color.md) 34 | 35 | AUTHOR 36 | ====== 37 | 38 | Vadim Belman 39 | 40 | -------------------------------------------------------------------------------- /docs/md/Vikna/Color/Named.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | 5 | 6 | `Vikna::Color::Named` - a role representing named colors 7 | 8 | DESCRIPTION 9 | =========== 10 | 11 | 12 | 13 | Stringifies into the given color name. 14 | 15 | ATTRIBUTES 16 | ========== 17 | 18 | 19 | 20 | ### `Str:D $.name` 21 | 22 | Color name used to create the object. 23 | 24 | METHODS 25 | ======= 26 | 27 | 28 | 29 | ### `rgb-by-name(Str:D $name)` 30 | 31 | If color with given `$name` is known then return a [`Hash`](https://docs.raku.org/type/Hash) with keys `r`, `g`, `b`. Otherwise returns empty hash. 32 | 33 | ### `known-color-name($name)` 34 | 35 | Returns *True* if color `$name` is known. 36 | 37 | SEE ALSO 38 | ======== 39 | 40 | [`Vikna`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna.md), [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md), [`Vikna::Color`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color.md), [`Color::Names`](https://modules.raku.org/dist/Color::Names) 41 | 42 | AUTHOR 43 | ====== 44 | 45 | Vadim Belman 46 | 47 | -------------------------------------------------------------------------------- /docs/md/Vikna/Color/RGB.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | 5 | 6 | `Vikna::Color::RGB` - a role for RGB representation of a color 7 | 8 | DESCRIPTION 9 | =========== 10 | 11 | 12 | 13 | Only defines stringification of the object into RGB triplet. 14 | 15 | SEE ALSO 16 | ======== 17 | 18 | [`Vikna`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna.md), [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md), [`Vikna::Color`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color.md) 19 | 20 | AUTHOR 21 | ====== 22 | 23 | Vadim Belman 24 | 25 | -------------------------------------------------------------------------------- /docs/md/Vikna/Color/RGBA.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | 5 | 6 | `Vikna::Color::RGBA` - a role for RGB with alpha representation of a color 7 | 8 | DESCRIPTION 9 | =========== 10 | 11 | 12 | 13 | Only defines stringification of the object into RGBA quadruplet. 14 | 15 | SEE ALSO 16 | ======== 17 | 18 | [`Vikna`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna.md), [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md), [`Vikna::Color`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Color.md) 19 | 20 | AUTHOR 21 | ====== 22 | 23 | Vadim Belman 24 | 25 | -------------------------------------------------------------------------------- /docs/md/Vikna/CommandHandling.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | 5 | 6 | `Vikna::CommandHandling` - role implementing command emission and processing. 7 | 8 | DESCRIPTION 9 | =========== 10 | 11 | 12 | 13 | This role unifies processing of `Event::Command` category of events. A command is the way external code communicate to a event handling object and this is how *kick and go* principle is implemented "in flesh". 14 | 15 | A command is an event which class is inheriting from `Event::Command`. It has two distinctive properties: *completion status* and *arguments*. 16 | 17 | *Arguments* is just a capture to invoke a command handler with. *Completion status* is a [`Promise`](https://docs.raku.org/type/Promise) which is kept with command handler return value. 18 | 19 | Command Handlers 20 | ---------------- 21 | 22 | A command handler is a method which would be invoked by event loop flow to react on a command event. For example: 23 | 24 | $widget.move: $x, $y; 25 | 26 | results in: 27 | 28 | * `Event::Cmd::SetGeom` is dispatched with a capture of `\($x, $y)` form 29 | 30 | * it passes all the usual stages of event dispatching 31 | 32 | * event loop invokes `cmd-setgeom` method, so that it receives two positionals from the capture above 33 | 34 | * `Event::Cmd::SetGeom` object is completed with `cmd-setgeom` result 35 | 36 | The method name of an event handler can be defined by the command event class using `cmd` method which should return the name. Otherwise method name is formed from the class name by stripping off the `Event::` namespace prefix. The rest of the event class name parts are lowercased and joined with `-`. This is how in the example above we get `cmd-setgeom` from `Event::Cmd::SetGeom`. Another example of the transformation is `Event::Cmd::Scroll::To` becomes `cmd-scroll-to`. 37 | 38 | If command handler of the given name doesn't exists then `CMD-FALLBACK` is tried. If found it is invoked with the only parameter – the command event itself. 39 | 40 | No handler for a command event it not an error situation. Such event would be silently ignored. 41 | 42 | METHODS 43 | ======= 44 | 45 | 46 | 47 | `multi event(Event::Command:D $ev)` 48 | ----------------------------------- 49 | 50 | Responsible for implementing the command handling. 51 | 52 | `multi send-event(Event::Command:U \evType, |args)` 53 | --------------------------------------------------- 54 | 55 | `multi send-event(Event::Command:U \evType, Capture:D $args)` 56 | ------------------------------------------------------------- 57 | 58 | `multi send-event(Event::Command:U \evType, Capture:D $args, %params)` 59 | ---------------------------------------------------------------------- 60 | 61 | The method is a [`Vikna::Event::Handling`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Event/Handling.md) `send-event` convenience wrapper. Similarly to `dispatcher` method, `send-command` creates an event object and passes it for event loop handling. The difference is that because a command must always be submitted to the object it is originated by, `send-command` bypasses `route-event` and submits directly into `send-event` method. 62 | 63 | `args` and `$args` captures are passed down to the command handle method. 64 | 65 | `%params` is used as event constructor profile. 66 | 67 | Returns `send-event` return value. 68 | 69 | SEE ALSO 70 | ======== 71 | 72 | [Vikna](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna.md), [Vikna::Manual](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md), [Vikna::CommandHandling](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/CommandHandling.md) 73 | 74 | AUTHOR 75 | ====== 76 | 77 | 78 | 79 | Vadim Belman 80 | 81 | -------------------------------------------------------------------------------- /docs/md/Vikna/Coord.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | 5 | 6 | `Vikna::Coord` - role representing a 2D coordinate 7 | 8 | DESCRIPTION 9 | =========== 10 | 11 | 12 | 13 | Only defines attributes `Int:D $.x` and `Int:D $.y`. 14 | 15 | SEE ALSO 16 | ======== 17 | 18 | [`Vikna`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna.md), [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md), 19 | 20 | AUTHOR 21 | ====== 22 | 23 | 24 | 25 | Vadim Belman 26 | 27 | -------------------------------------------------------------------------------- /docs/md/Vikna/OS.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | 5 | 6 | `Vikna::OS` - base role for OS-specific layer class 7 | 8 | ATTRIBUTES 9 | ========== 10 | 11 | 12 | 13 | ### [`Vikna::Screen`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Screen.md) `$.screen` 14 | 15 | Screen driver. 16 | 17 | REQUIRED METHODS 18 | ================ 19 | 20 | ### `build-screen()` 21 | 22 | Method must construct and return a [`Vikna::Screen`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Screen.md) object to initialize `$.screen`. See [`AttrX::Mooish`](https://modules.raku.org/dist/AttrX::Mooish) for lazy attributes implementation. 23 | 24 | ### `inputs()` 25 | 26 | Method is expected to return a list of [`Vikna::EventEmitter`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/EventEmitter.md) objects for each OS-provided input device like a mouse or a keyboard. 27 | 28 | SEE ALSO 29 | ======== 30 | 31 | [`Vikna`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna.md), [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md), [`Vikna::Classes`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Classes.md) 32 | 33 | AUTHOR 34 | ====== 35 | 36 | Vadim Belman 37 | 38 | -------------------------------------------------------------------------------- /docs/md/Vikna/Object.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | `Vikna::Object` - the base class of most of `Vikna` classes 5 | 6 | DESCRIPTION 7 | =========== 8 | 9 | 10 | 11 | This class implements the basic functionality required by many of `Vikna` classes. 12 | 13 | ATTRIBUTES 14 | ========== 15 | 16 | 17 | 18 | `$.app` 19 | ------- 20 | 21 | The application this object has been created under. For example, a typical access to the desktop object is done via: 22 | 23 | $.app.desktop 24 | 25 | `Int $.id` 26 | ---------- 27 | 28 | Integer unique object id. Implementation dependant and thus one can't rely upon its current implementation. Only makes sense in object comparison, caching, etc. 29 | 30 | Lazy, built by `build-id` method. See [`AttrX::Mooish`](https://modules.raku.org/dist/AttrX::Mooish:cpan:VRURG). 31 | 32 | METHODS 33 | ======= 34 | 35 | 36 | 37 | `new(*%c)` 38 | ---------- 39 | 40 | Acts more as a wrapper to the standard method new. This is where profiles are merged and the result is then used to call the standard `new` method. 41 | 42 | `create(Mu \type, |c)` 43 | ---------------------- 44 | 45 | This method has to be used by any `Vikna::Object` descendant to create a new Vikna object. The method guarantees that the application object will be propagated to all newly created instances. 46 | 47 | `make-object-profile(%c)` 48 | ------------------------- 49 | 50 | This method implements the magic of object profiles. But before getting into details, I recommend to read about RMRO in [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md) unless already done so. 51 | 52 | The argument is the profile as it is supplied to the constructor. 53 | 54 | In details, the method: 55 | 56 | * requests for configuration profile from the application object if known; 57 | 58 | * walks over `profile-default` submethods in reverse RMRO order and collects default profiles. Then merges them into a single default profile hash; 59 | 60 | * walks over `profile-checkin` submethods in reverse RMRO order and invokes them with the destination profile hash object, constructor-supplied profile hash, the merged default profile, and config 61 | 62 | The hashes are merged using deep merge as implemented by `Hash::Merge` module. Due to reverse RMRO order used, children classes can override what's provided by parents or roles consumed. For example: 63 | 64 | class MyParent is Vikna::Object { 65 | submethod profile-default { 66 | foo => 42, 67 | } 68 | } 69 | class MyChild is MyParent { 70 | submethod profile-default { 71 | foo => "is the answer!", 72 | } 73 | } 74 | 75 | In this case the final default profile will be: 76 | 77 | { foo => "is the answer!", } 78 | 79 | Things work similarly for `profile-checkin`. 80 | 81 | `submethod profile-default` 82 | --------------------------- 83 | 84 | Returns just an empty hash as a seed for children profiles. 85 | 86 | Must not be invoked by a user. 87 | 88 | `submethod profile-checkin(%profile, %constructor, %default, %config)` 89 | ---------------------------------------------------------------------- 90 | 91 | Merges profiles `%default`, `%config`, `%constructor` using `Hash::Merge` in the order given into `%profile`. This way we get the first iteration of the final profile hash as it will be used to instantiate a new object. Note that the merging order actually defines priorities of the profile sources making the constructor arguments the most important of all. 92 | 93 | All children `profile-checkin` submethods are invoked with the same parameters. It makes the initial state available to any child. It allows to override any changes done to `%profile` by a parent submethod even if they wipe out initial keys or change their values. 94 | 95 | Must not be invoked by a user. 96 | 97 | `name()` 98 | -------- 99 | 100 | Returns object name. The standard name is formed of object's class name and it's `$.id` attribute. But can be overriden with corresponding constructor parameter. 101 | 102 | `multi method throw(X::Base:U \exception, *%args)` 103 | -------------------------------------------------- 104 | 105 | `multi method throw(X::Base:D $exception)` 106 | ------------------------------------------ 107 | 108 | Throws an exception. Because all Vikna exceptions are recording the object which thrown them, this method is a convenient shortcut which passes the required parameter to the exception constructor: 109 | 110 | class X::MyException is X::Base { ... } 111 | ... 112 | $obj.throw: X::MyException, :details($info); 113 | 114 | The second candidate of the method simply rethrows a pre-created exception object. 115 | 116 | `multi method fail(X::Base:U \exception, *%args)` 117 | ------------------------------------------------- 118 | 119 | `multi method fail(X::Base:D $exception)` 120 | ----------------------------------------- 121 | 122 | Similar to `throw` above, but invokes `fail` with the exception object. 123 | 124 | `trace(|)` 125 | ---------- 126 | 127 | This method is a shortcur to [`Vikna::App`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/App.md) method `trace`. It passes the invoking object alongside with the arguments capture. 128 | 129 | `flow(&code, Str :$name, :$sync = False, :$branch = False, :$in-context = False, Capture:D :$args = \())` 130 | --------------------------------------------------------------------------------------------------------- 131 | 132 | Creates a new code flow (see [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md)). A flow can be created either as an asynchronous one running in its own thread; or as a synchronous, invoked on the current thread context. The flow will execute `&code` with arguments from `:args` parameter. 133 | 134 | It is often the case that when a new thread is spawned it doesn't have access to the dynamic context of the code which spawned the thread. With `:in-context` argument the flow records its caller's context allowing to search for dynamics in it using hash-key syntax: 135 | 136 | if $*VIKNA-FLOW<$*VIKNA-CURRENT-EVENT>:exists { 137 | $cur-event = $*VIKNA-FLOW<$*VIKNA-CURRENT-EVENT>; 138 | } 139 | 140 | When the flow is created withing a "parent" flow, it would try to chain the search for a dynamic symbol with the "parent". 141 | 142 | *NOTE* that the feature is a potential memory hog as it might keep many upstream closures referenced even when nobody else is not using them anymore. 143 | 144 | If a new flow is created with `:branch` parameter then it would implicitly take the name of the enclosing flow and will get `:in-context` parameter implicitly. 145 | 146 | The method returns a promise which would be kept with flow's return value. 147 | 148 | `allocate-flow`, `free-flow` 149 | ---------------------------- 150 | 151 | Internal implementation details. 152 | 153 | `panic(Exception:D $cause)` 154 | --------------------------- 155 | 156 | Standard method to bail out in case of problems. Basically, overriden by higher-order classes. 157 | 158 | `Str` 159 | ----- 160 | 161 | Shortcuts to `name` method. 162 | 163 | SEE ALSO 164 | ======== 165 | 166 | [`Vikna`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna.md), [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md) 167 | 168 | AUTHOR 169 | ====== 170 | 171 | 172 | 173 | Vadim Belman 174 | 175 | -------------------------------------------------------------------------------- /docs/md/Vikna/Parent.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | 5 | 6 | `Vikna::Parent` – role providing parent object interface 7 | 8 | DESCRIPTION 9 | =========== 10 | 11 | 12 | 13 | The role implements essential parent object functionality with focus on Vikna needs. 14 | 15 | ATTRIBUTES 16 | ========== 17 | 18 | 19 | 20 | `Array:D @.strata` 21 | ------------------ 22 | 23 | Two-dimensional array of children. First dimension are strata whose indices are aliased with `StBack`, `StMain`, `StModal` enums. Second dimension are children bound to corresponding stratum. Order of children in a stratum array defines their Z-order when drawn on the parent widget. 24 | 25 | METHODS 26 | ======= 27 | 28 | 29 | 30 | `is-my-child($child)` 31 | --------------------- 32 | 33 | Checks if `$child` is known by the parent object. If not then throws `X::NoChild`. 34 | 35 | `elems(ChildStrata $stratum?)` 36 | ------------------------------ 37 | 38 | If `$stratum` parameter is defined returns number of children in the corresponding stratum. Otherwise returns number of all children in all strata. 39 | 40 | `multi children(ChildStrata $stratum?, :$reverse, :lazy)` 41 | --------------------------------------------------------- 42 | 43 | `multi children(ChildStrata $stratum?, :$reverse, :!lazy)` 44 | ---------------------------------------------------------- 45 | 46 | Returns children object. If `$stratum` is defined then returns only those bound to the requested stratum. With `:lazy` parameter returns a lazy sequence of children. Otherwise returns a list. 47 | 48 | `add-child($child, ChildStrata:D $stratum = StMain)` 49 | ---------------------------------------------------- 50 | 51 | Adds a new child to the `$stratum`. Child's parent is set to the parent object with `set-parent` method. 52 | 53 | `remove-child($child)` 54 | ---------------------- 55 | 56 | Removes a child if known, otherwise throws `X::NoChild`. Child's parent is reset to `Nil`. 57 | 58 | `next-to($child, :$reverse, :$on-strata, :$loop)` 59 | ------------------------------------------------- 60 | 61 | Returns a child object next to `$child` in Z-order. With `:on-strata` passes cross-stratum boundaries in `StBack` -> `StMain` -> `StModal` order. Otherwise only considers the stratum child is bound to. With `:loop` returns the first child if method is invoked for the last child in Z-order, with respect to `:on-strata`. 62 | 63 | Uses `is-my-child` method to check if child is known. 64 | 65 | `to-top($child)` 66 | ---------------- 67 | 68 | Moves a child to the top of Z-order. Only operates within child's stratum. 69 | 70 | `to-bottom($child)` 71 | ------------------- 72 | 73 | Moves a child to the bottom of Z-order. Only operates within child's stratum. 74 | 75 | `is-topmost($child, :$on-strata)`, `is-bottommost($child, :$on-strata)` 76 | ----------------------------------------------------------------------- 77 | 78 | Returns true if the child is on the top or bottom of Z-order respectively. With `:on-strata` both check if the condition is met globally. 79 | 80 | `child-stratum($child --> ChildStrata)` 81 | --------------------------------------- 82 | 83 | Returns the stratum the child is bound to. Returns `Nil` if the child is not known to the parent object. 84 | 85 | `for-children(&code, :&pre, :&post, :$reverse, :$stratum)` 86 | ---------------------------------------------------------- 87 | 88 | Method iterates over children within a lock-protected loop. I.e. it is thread-safe children iteration. 89 | 90 | `:pre` and `:post` code is executed before and after the loop correspondingly. `:reverse` causes it to iterate in reverse Z-order. With `:stratum` it iterates only over the specific stratum. 91 | 92 | Nomal control flow routines `next` and `last` can be used within `&code`: 93 | 94 | self.for-children: -> $child { 95 | if $child.name ~~ $pattern { 96 | self.found-it($child); 97 | last 98 | } 99 | }; 100 | 101 | `children-protect(&code)` 102 | ------------------------- 103 | 104 | Similarly to [`Lock`](https://docs.raku.org/type/Lock) `protect` method, lock-protects `&code`. The protection guarantees safety of operations with childrens. 105 | 106 | **IMPORTANT!** None of the above mentioned methods, except for `for-children`, are lock protected! This is to avoid performance penalty of lock protection for cases where it is not needed. For example, when certain operation is only ever performed under event loop control. 107 | 108 | SEE ALSO 109 | ======== 110 | 111 | [`Vikna`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna.md), [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md) 112 | 113 | AUTHOR 114 | ====== 115 | 116 | 117 | 118 | Vadim Belman 119 | 120 | -------------------------------------------------------------------------------- /docs/md/Vikna/Point.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | 5 | 6 | `Vikna::Point` - geometric point type 7 | 8 | SYNOPSIS 9 | ======== 10 | 11 | 12 | 13 | my $p1 = Vikna::Point(13, 42); my $p2 = Vikna::Point(1, 1); my $p3 = $p1 + $p2; 14 | 15 | DESCRIPTION 16 | =========== 17 | 18 | 19 | 20 | Implements basic point operations required by the framework. 21 | 22 | Does [`Vikna::Coord`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Coord.md). 23 | 24 | METHODS 25 | ======= 26 | 27 | 28 | 29 | `multi new(Int:D $x, Int:D $y, *%c)` 30 | ------------------------------------ 31 | 32 | Shortcut to `Vikna::Point.new(:$x, :$y)` way of creating a new point object. 33 | 34 | Also, `Vikna::Point(...args...)` is equivalent to `Vikna::Point.new(...args...)`. 35 | 36 | `multi relative-to(Int:D $x, Int:D $y)` 37 | --------------------------------------- 38 | 39 | `multi relative-to(Vikna::Coord:D $point)` 40 | ------------------------------------------ 41 | 42 | Returns a new point object which represents a difference between `self` and the coordinates in the argument(s). 43 | 44 | Consider it a vector subtraction. 45 | 46 | `multi aboslute(Int:D $x, Int:D $y)` 47 | ------------------------------------ 48 | 49 | `multi absolute(Vikna::Coord:D $point)` 50 | --------------------------------------- 51 | 52 | Given that `self` represents a relative point, creates a new point object which represent position of `self` in the coordinate system to which argument(s) belong. 53 | 54 | Consider it a vector addition. 55 | 56 | `Str` 57 | ----- 58 | 59 | Returns a string representation of the point. 60 | 61 | `gist` 62 | ------ 63 | 64 | Same as `Str` 65 | 66 | OPERATORS 67 | ========= 68 | 69 | 70 | 71 | `infix:<+>`, `infix:<->` 72 | ------------------------ 73 | 74 | Vector addition and subtraction of two points. 75 | 76 | SEE ALSO 77 | ======== 78 | 79 | [Vikna](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna.md), [Vikna::Manual](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md), [Vikna::Coord](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Coord.md) 80 | 81 | AUTHOR 82 | ====== 83 | 84 | 85 | 86 | Vadim Belman 87 | 88 | -------------------------------------------------------------------------------- /docs/md/Vikna/Rect.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | 5 | 6 | `Vikna::Rect` - the rectangle type 7 | 8 | SYNOPSIS 9 | ======== 10 | 11 | 12 | 13 | my $rect = Vikna::Rect(10, 5, 42, 13); 14 | my $rect2 = $rect.move-by(-5, 5); # 5, 10, 42, 13 15 | $rect.contains($rect2); # False 16 | 17 | DESCRIPTION 18 | =========== 19 | 20 | 21 | 22 | Does [`Vikna::Coord`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Coord.md). 23 | 24 | Represents a rectangle object. 25 | 26 | ATTRIBUTES 27 | ========== 28 | 29 | 30 | 31 | ### `UInt:D $.w`, `UInt:D $.h` 32 | 33 | Width and height of the rectangle, respectively. 34 | 35 | ### `Int $.right`, `Int $.bottom` 36 | 37 | Right and bottom widget boundaries. 38 | 39 | METHODS 40 | ======= 41 | 42 | 43 | 44 | ### `new($x, $y, $w, $h)` 45 | 46 | A shortcut to `new(:$x, :$y, :$w, :$h)`. The class also supports a callable for of instantiation, as shown in [SYNPOSIS](#SYNPOSIS). 47 | 48 | ### `dup(*%twiddles)` 49 | 50 | Duplicates a rectangle instance using `new`. 51 | 52 | ### `Array()` 53 | 54 | Coerces a rectangle into an array of it's coordinates and dimensions. 55 | 56 | ### `List()` 57 | 58 | Similar to `Array` method above, but coerce into a `List`. 59 | 60 | ### `coords()` 61 | 62 | An alias to `List` method. 63 | 64 | ### `multi overlap($x, $y, $w, $h)` 65 | 66 | ### `multi overlap(Vikna::Rect:D $rec)` 67 | 68 | Returns *True* if two rectangles overlap. 69 | 70 | ### `multi clip-by(Vikna::Rect:D $into, :$copy?)` 71 | 72 | ### `multi clip-by(Int:D $x, Int:D $y, UInt:D $w, UInt:D $h)` 73 | 74 | Clip a rectangle by `$into`. 75 | 76 | ### `multi dissect(Vikna::Rect:D $by)` 77 | 78 | ### `multi dissect(Int:D $x, Int:D $y, UInt:D $w, UInt:D $h)` 79 | 80 | Dissect a rectangle with `$by`. It means that `$by` is cut out of the rectangle we dissect and the remaining area is dissected into sub-rectangles. 81 | 82 | ### `multi dissect(@by)` 83 | 84 | Dissect by a list of rectangles. 85 | 86 | ### `multi contains(Int:D $x, Int:D $y)` 87 | 88 | ### `multi contains(Int:D $x, Int:D $y, UInt:D $w, UInt:D $h)` 89 | 90 | ### `multi contains(Vikna::Coord:D $point)` 91 | 92 | ### `multi contains(Vikna::Rect:D $rect)` 93 | 94 | Returns *True* is the argument is contained by rectangle. 95 | 96 | ### `multi relative-to(Int:D $x, Int:D $y)` 97 | 98 | ### `multi relative-to(Int:D $x, Int:D $y, UInt:D $w, UInt:D $h, :$clip = False)` 99 | 100 | ### `multi relative-to(Vikna::Coord:D $point)` 101 | 102 | ### `multi relative-to(Vikna::Rect:D $rect, :$clip = False)` 103 | 104 | Takes current rectangle and returns a new one which coordinates are relative to coordinates of `$rect`. With `:clip` clips the new rectangle by `$rect`. 105 | 106 | ### `multi absolute(Int:D $x, Int:D $y)` 107 | 108 | ### `multi absolute(Vikna::Rect:D $rec, :$clip = False)` 109 | 110 | Assuming that rectangle coordinates are relative to the argument, transforms them into the "absolute" values and returns a new rectangle. With `:clip` it is cliped by `$rect`. 111 | 112 | ### `multi move(Int:D $x, Int:D $y)` 113 | 114 | ### `multi move(Vikna::Coord:D $point)` 115 | 116 | Returns a new rectangle with it's origin set to the argument. 117 | 118 | ### `multi move-by(Int:D $dx, Int:D $dy)` 119 | 120 | ### `multi move-by(Vikna::Coord:D $delta)` 121 | 122 | Returns a new rectangle shifted by the argument. 123 | 124 | ### `Str()` 125 | 126 | ### `gist()` 127 | 128 | Strigify rectangle. 129 | 130 | OPERATORS 131 | ========= 132 | 133 | 134 | 135 | ### `infix:<+>(Vikna::Rect:D $r, Vikna::Coord:D $delta)` 136 | 137 | Same as `move-by`. 138 | 139 | ### `infix:<==>(Vikna::Rect:D $a, Vikna::Rect:D $b)` 140 | 141 | Returns *True* if both rectangles have same origins and dimensions. 142 | 143 | SEE ALSO 144 | ======== 145 | 146 | [`Vikna`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna.md), [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md), [`Vikna::Classes`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Classes.md), [`Vikna::Coord`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Coord.md), [`Vikna::Point`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Point.md) 147 | 148 | AUTHOR 149 | ====== 150 | 151 | Vadim Belman 152 | 153 | -------------------------------------------------------------------------------- /docs/md/Vikna/WAttr.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | 5 | 6 | `Vikna::WAttr` - widget attributes 7 | 8 | DESCRIPTION 9 | =========== 10 | 11 | 12 | 13 | Inherits from [Vikna::CAttr](Vikna::CAttr). 14 | 15 | Class represents default widget attributes. 16 | 17 | ATTRIBUTES 18 | ========== 19 | 20 | 21 | 22 | ### `$.pattern` 23 | 24 | Background pattern of a widget. Interpretation of this attribute depends on a particular widget. But [`Vikna::Widget`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Widget.md) defines it as a string which fills the background. Say, if set to *'.'* then the background will be filled with dots. 25 | 26 | ROUTINES 27 | ======== 28 | 29 | 30 | 31 | ### `multi sub wattr($fg, $bg?, $style?, $pattern?)` 32 | 33 | ### `multi sub wattr(:$fg, :$bg?, :$style?, :$pattern?)` 34 | 35 | Shortcut to create a `Vikna::WAttr` instance. 36 | 37 | SEE ALSO 38 | ======== 39 | 40 | [`Vikna`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna.md), [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md), 41 | 42 | AUTHOR 43 | ====== 44 | 45 | Vadim Belman 46 | 47 | -------------------------------------------------------------------------------- /docs/md/Vikna/X.md: -------------------------------------------------------------------------------- 1 | NAME 2 | ==== 3 | 4 | 5 | 6 | `Vikna::X` - collection of exception classes 7 | 8 | DESCRIPTION 9 | =========== 10 | 11 | 12 | 13 | Please, consult the source for more information about an exception than is provided by this documentation. Exceptions are far from being stabilized and documenting all of them doesn't make big sense yet. 14 | 15 | Class `X::Base` 16 | =============== 17 | 18 | Is [`Exception`](https://docs.raku.org/type/Exception). The base class of all Vikna exceptions. Define a single required attribute `$.obj` which points to the object which method thrown the exception. 19 | 20 | SEE ALSO 21 | ======== 22 | 23 | [`Vikna`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna.md), [`Vikna::Manual`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Manual.md), [`Vikna::Point`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Point.md), [`Vikna::Rect`](https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Rect.md) 24 | 25 | AUTHOR 26 | ====== 27 | 28 | Vadim Belman 29 | 30 | -------------------------------------------------------------------------------- /examples/button.raku: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | use Vikna::App; 3 | use Vikna::Window; 4 | use Vikna::Label; 5 | use Vikna::Events; 6 | use Vikna::Utils; 7 | use Vikna::Button; 8 | use Vikna::TextScroll; 9 | use AttrX::Mooish; 10 | 11 | class ButWin is Vikna::Window { 12 | multi method event(Event::Button::Press:D $ev) { 13 | self.set-title: "Button " ~ $ev.origin.name ~ " press " ~ now.DateTime.local.hh-mm-ss; 14 | } 15 | } 16 | 17 | class JumpBut is Vikna::Button { 18 | # proto method event(::?CLASS:D: Event:D) {*} 19 | multi method event(Event::Button::Press:D $ev) { 20 | my $next-win = $.parent.group.next-sibling: :loop; 21 | self.trace: "Switching to parent ", $next-win; 22 | my $vf = $*VIKNA-FLOW; 23 | $.detach.head.completed.then: { 24 | my $*VIKNA-FLOW = $vf; 25 | self.trace: "Adding myself to widget ", $next-win; 26 | $next-win.add-child: self; 27 | $.target = $next-win; 28 | }; 29 | } 30 | } 31 | 32 | class ButApp is Vikna::App { 33 | method main { 34 | my $w = ($.desktop.w / 2).Int; 35 | my $dx = ($.desktop.w / 2).ceiling; 36 | for ^2 -> $i { 37 | my $win = $.desktop.create-child: ButWin, 38 | :name("Win$i"), :title("Window $i"), 39 | :x( $dx * $i ), :5y, :$w, :10h, 40 | ; 41 | my $wcw = $w - 2; 42 | my $wdx = (($wcw / 2 - 10) / 2).Int; 43 | $win.create-child: JumpBut, 44 | :name("ButOk$i"), :x($wdx), :y($i + 2), :10w, 45 | :text("Ok$i"), 46 | :target($win), 47 | ; 48 | $win.create-child: Vikna::Button, 49 | :name("ButCancel$i"), :x($w - 2 - $wdx - 10), :y($i + 2), # :10w, 50 | :text("Cancel$i"), 51 | :target($win), 52 | :use3d, 53 | ; 54 | } 55 | } 56 | } 57 | 58 | ButApp.new( :!debugging ).run; 59 | -------------------------------------------------------------------------------- /examples/input-line.raku: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | 3 | use Vikna::App; 4 | use Vikna::Window; 5 | use Vikna::Label; 6 | use Vikna::InputLine; 7 | 8 | class IApp is Vikna::App { 9 | method main { 10 | my $iw = $.desktop.create-child: 11 | Vikna::Window, 12 | :5x, :3y, :60w, :7h, 13 | :name, 14 | ; 15 | for ^2 -> $field { 16 | $iw.create-child: 17 | Vikna::Label, 18 | :1x, :y( $field*2 + 1 ), :10w, 19 | :text("Field $field:") 20 | ; 21 | my $in = $iw.create-child: 22 | Vikna::InputLine, 23 | :12x, :y( $field*2 + 1 ), 24 | :name("Field" ~ $field), 25 | ; 26 | } 27 | } 28 | } 29 | 30 | IApp.new( :!debugging ).run; 31 | -------------------------------------------------------------------------------- /examples/mouse-basic.raku: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | use Vikna::App; 3 | use Vikna::Window; 4 | use Vikna::Events; 5 | use Vikna::TextScroll; 6 | use Vikna::PointerTarget; 7 | 8 | # note Vikna::Window.^mro.map( *.^name ).join(", "); 9 | # exit; 10 | 11 | class MRep 12 | is Vikna::TextScroll 13 | does Vikna::PointerTarget 14 | is Vikna::Focusable { 15 | submethod profile-default { 16 | attr => { 17 | :fg(''), :bg(''), :pattern(' '), 18 | # :fg, :bg, :pattern(' ') 19 | }, 20 | # focused-attr => { 21 | # :fg, :bg, :pattern<_> 22 | # } 23 | } 24 | multi method event(::?CLASS:D: Event::Attached:D $ev) { 25 | if $ev.child === self { 26 | self.subscribe: $.parent.parent, -> $pev { 27 | given $pev { 28 | when Event::ZOrderish { 29 | self.say: $pev 30 | } 31 | when Event::Focus::In || Event::Focus::Out { 32 | self.say: $pev 33 | } 34 | } 35 | } 36 | } 37 | nextsame 38 | } 39 | multi method event(::?CLASS:D: Event::Mouse:D $ev) { 40 | self.say: $ev; 41 | nextsame 42 | } 43 | multi method event(::?CLASS:D: Event::Mouse::Enter:D $ev) { 44 | self.say: $ev; 45 | nextsame 46 | } 47 | multi method event(::?CLASS:D: Event::Mouse::Leave:D $ev) { 48 | self.say: $ev; 49 | nextsame 50 | } 51 | multi method event(::?CLASS:D: Event::ZOrderish:D $ev) { 52 | self.say: $ev; 53 | nextsame 54 | } 55 | } 56 | 57 | class MWin is Vikna::Window { 58 | multi method event(::?CLASS:D: Event::Attached:D $ev) { 59 | self.trace: "GOT ATTACHED FOR ", $ev.child.name; 60 | if $ev.child === $.client { 61 | self.subscribe-to-child: 62 | self.create-child: MRep, :0x, :0y, w => $.client.w, h => $.client.h, :name("MRep" ~ $.name), :wrap, ; 63 | } 64 | nextsame; 65 | } 66 | # multi method event(::?CLASS:D: Event::Attached:D $ev) { 67 | # self.trace: "HAVE ATTACHED from { $ev.origin }: ", $ev.child.name, " to ", $ev.parent.name; 68 | # if $ev.child ~~ MRep { 69 | # start { 70 | # for ^3 { 71 | # $ev.child.say: "Line $_" 72 | # } 73 | # } 74 | # } 75 | # } 76 | } 77 | 78 | # note "MWin mro: ", MWin.^mro_unhidden.map( *.^name ).join(", "); 79 | # exit; 80 | 81 | class MApp is Vikna::App { 82 | has @!w; 83 | method main { 84 | for ^3 { 85 | my $w = ($.desktop.w * 2 / 3 ).Int; 86 | my $h = 15; 87 | my $dy = (($.desktop.h - $h) / 2).Int; 88 | my $dx = ($.desktop.w / 10).Int; 89 | if $_ == 0 { 90 | $w = ($.desktop.w * 7 / 8 ).Int; 91 | $h = ($.desktop.h / 2).Int; 92 | } 93 | @!w.push: $.desktop.create-child: 94 | MWin, :x($_ * $dx), :y($_ * $dy), :$w, :$h, :name('Window' ~ $_), :title('Window #' ~ $_), :pattern(~$_); 95 | } 96 | } 97 | } 98 | 99 | MApp.new(:!debugging).run; 100 | -------------------------------------------------------------------------------- /examples/threaded-rainbow-group.raku: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env raku 2 | 3 | # NOTE! This example is pushing the limits of an all-native Raku application. It is not recommended to be ran in a 4 | # console with more than 90 columns and 30 lines. On slow systems event smaller window is recommended. In either case, 5 | # be patient, please! 6 | 7 | use v6.e.PREVIEW; 8 | use Vikna::Label; 9 | use Vikna::App; 10 | use Vikna::TextScroll; 11 | use Vikna::Events; 12 | use Vikna::Widget::Group; 13 | use Vikna::Widget::GroupMember; 14 | use Vikna::Utils; 15 | use AttrX::Mooish; 16 | use Async::Workers; 17 | 18 | BEGIN $*SCHEDULER = ThreadPoolScheduler.new(:max_threads(2000)); 19 | 20 | constant WAVES = 2; 21 | 22 | class Event::ColorRotate is Event::Informative { } 23 | 24 | class ColorLabel is Vikna::Widget::GroupMember is Vikna::Label { 25 | multi method event(Vikna::Event::Redrawn:D $ev) { 26 | if $.initialized { 27 | $.app.reporter.print: $.name, " rdrwn \r"; 28 | } 29 | nextsame 30 | } 31 | } 32 | 33 | class Rainbow is Vikna::Widget::Group { 34 | has $.lw = 6; 35 | has $.max-dist is mooish(:lazy); 36 | has $.color-shift = 0; 37 | has %!childp; 38 | has $.loop; 39 | has $!rainbow-sh = get-sync-handle("rainbow"); 40 | has Async::Workers:D $!wm .= new(:max-workers(($*KERNEL.cpu-cores * 2) max 1)); 41 | 42 | method build-max-dist { 43 | $.w min $.h 44 | # sqrt(($.w div $!lw)² + $.h²) 45 | } 46 | 47 | method l-color($x, $y, $shift) { 48 | my $R = sqrt($x² + $y²); 49 | my \D = π × WAVES × 2 × $R / $!max-dist; 50 | sub clr($phase --> UInt:D) { 51 | (255 * ((1 + sin(D + π × $phase / 3 + $shift)) / 2)).Int 52 | } 53 | my $bg = (^3).map({ clr($_) }).join(","); 54 | } 55 | 56 | multi method event(Event::Init:D $ev) { 57 | self.flatten-sync: $!rainbow-sh, { 58 | self.flow: :name('Fill the rainbow'), :in-context, { 59 | my $xcount = $.w div 6; 60 | my $ycount = $.h; 61 | my $reporter = $.app.reporter; 62 | for ^$xcount -> $x { 63 | $reporter.say: "X: ", $x.fmt('%10d'); 64 | for ^$.h -> $y { 65 | my $idx = $x.fmt('%02d') ~ $y.fmt('%02d'); 66 | sync-on self.create-member: 67 | ColorLabel, :w($!lw), :h(1), :x($x × $!lw), :$y, :attr{ 68 | :fg, :bg(self.l-color($x, $y, 0)), :pattern(' ') 69 | }, :text($idx), :name("L" ~ $idx); 70 | # self.set-await: $c; 71 | #, $c.updated; 72 | } 73 | } 74 | $reporter.say: "DONE!"; 75 | } 76 | } 77 | nextsame 78 | } 79 | 80 | multi method event(Event::ColorRotate:D $ev) { 81 | $!color-shift += π / 3; 82 | my $reporter = $.app.reporter; 83 | $reporter.say: "SHIFT: ", $!color-shift.fmt('%.4f'); 84 | my $now = now; 85 | if $!loop { 86 | $reporter.say: "LOOP: ", ($now - $!loop).fmt('%.2f'), " sec."; 87 | } 88 | $!loop = $now; 89 | self.flatten-sync: $!rainbow-sh, { 90 | self.flow: :name('SHIFT'), :in-context, { 91 | self.invalidate; 92 | my @evp; 93 | self.for-children: -> $c { 94 | $!wm.do-async: flow-branch({ 95 | if $c ~~ ColorLabel { 96 | my $bg = self.l-color($c.x div $!lw, $c.y, $!color-shift); 97 | $c.set-color: :fg, :$bg; 98 | $c.invalidate; 99 | $c.redraw; 100 | } 101 | }) 102 | } 103 | } 104 | } 105 | $.app.reporter.say: "Shifted"; 106 | } 107 | 108 | multi method event(Vikna::Event::ClockSignal:D $ev) { 109 | if $ev.dispatcher === self { 110 | self.dispatch: Event::ColorRotate; 111 | $.app.reporter.say: "FLATTENED ", self.children.elems, " children"; 112 | } 113 | } 114 | } 115 | 116 | class ThreadApp is Vikna::App { 117 | has $.reporter; 118 | method main(|) { 119 | constant INFO-W = 25; 120 | $!reporter = $.desktop.create-child: 121 | Vikna::TextScroll, StBack, 122 | x => $.desktop.w - INFO-W, 123 | y => 1, 124 | :w(INFO-W), :h($.desktop.h - 1), 125 | :name, 126 | :attr{ 127 | :pattern(' '), :bg(0x10, 0x10, 0x10), :fg; 128 | }; 129 | my $tt = $.desktop.create-child: Vikna::Label, 130 | :name, 131 | :x($.desktop.w - INFO-W), :y(0), :w(INFO-W), :h(1), 132 | :text('tick-tock'), 133 | :fg, :bg(0x80, 0x80, 0x80); 134 | $.desktop.create-child: 135 | Rainbow, 136 | :x(0), :y(0), 137 | :w($.desktop.w - 25), :h($.desktop.h), 138 | # :w(48), :h(8), 139 | ; 140 | for ^Inf -> $counter { 141 | sleep 1; 142 | $tt.set-text: "tick-tok " ~ $counter.fmt('%8d'); 143 | } 144 | } 145 | } 146 | 147 | my $tapp = ThreadApp.new(:!debugging); 148 | $tapp.run; 149 | -------------------------------------------------------------------------------- /examples/threaded-rainbow.raku: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env raku 2 | 3 | # NOTE! This example is pushing the limits of an all-native Raku application. It is not recommended to be ran in a 4 | # console with more than 90 columns and 30 lines. On slow systems event smaller window is recommended. In either case, 5 | # be patient, please! 6 | 7 | use v6.e.PREVIEW; 8 | use Vikna::Label; 9 | use Vikna::App; 10 | use Vikna::TextScroll; 11 | use Vikna::Events; 12 | use Vikna::Utils; 13 | use AttrX::Mooish; 14 | 15 | BEGIN $*SCHEDULER = ThreadPoolScheduler.new(:max_threads(2000)); 16 | 17 | constant WAVES = 2; 18 | 19 | class Event::ColorRotate is Event::Informative { } 20 | 21 | class ColorLabel is Vikna::Label { 22 | multi method event(Vikna::Event::Redrawn:D $ev) { 23 | if $.initialized { 24 | $.app.reporter.print: $.name, " rdrwn \r"; 25 | } 26 | nextsame 27 | } 28 | } 29 | 30 | class Rainbow is Vikna::Widget { 31 | has $.lw = 6; 32 | has $.max-dist is mooish(:lazy); 33 | has $.color-shift = 0; 34 | has %!childp; 35 | has atomicint $!all-added = 0; 36 | has atomicint $!status-code = 0; 37 | has $.loop; 38 | 39 | method build-max-dist { 40 | $.w min $.h 41 | # sqrt(($.w div $!lw)² + $.h²) 42 | } 43 | 44 | method l-color($x, $y, $shift) { 45 | my $R = sqrt($x² + $y²); 46 | my \D = π × WAVES × 2 × $R / $!max-dist; 47 | sub clr($phase --> UInt:D) { 48 | (255 * ((1 + sin(D + π × $phase / 3 + $shift)) / 2)).Int 49 | } 50 | my $bg = (^3).map({ clr($_) }).join(","); 51 | } 52 | 53 | method cmd-refresh { 54 | $.app.reporter.say: "cmd refresh, ", $.flatten-blocked; 55 | callsame; 56 | $.app.reporter.say: " - refreshed "; 57 | } 58 | 59 | method cmd-childcanvas(Vikna::Widget:D $child, |) { 60 | callsame; 61 | with %!childp{$child.id} { 62 | if .status ~~ Planned { 63 | .keep(True); 64 | } 65 | } 66 | } 67 | 68 | method set-await(Vikna::Widget:D $child, $promise = Promise.new) { 69 | %!childp{$child.id} = $promise; 70 | ++⚛$!all-added; 71 | } 72 | 73 | method await-redraws { 74 | $!status-code ⚛= 1; 75 | self.flow: :name('Await children'), { 76 | await %!childp.values; 77 | %!childp = (); 78 | self.flatten-unblock; 79 | $.app.reporter.say: "\nAll ", self.elems, " done, next, ", self.flatten-blocked; 80 | $!status-code ⚛= 2; 81 | } 82 | } 83 | 84 | multi method event(Event::Init:D $ev) { 85 | self.flatten-block; 86 | self.flow: :name('Fill the rainbow'), { 87 | my $xcount = $.w div 6; 88 | my $ycount = $.h; 89 | my $reporter = $.app.reporter; 90 | for ^$xcount -> $x { 91 | $reporter.say: "X: ", $x.fmt('%10d'); 92 | for ^$.h -> $y { 93 | my $idx = $x.fmt('%02d') ~ $y.fmt('%02d'); 94 | my $c = self.create-child: 95 | ColorLabel, 96 | :w($!lw), :h(1), :x($x × $!lw), :$y, :attr{ 97 | :fg, :bg(self.l-color($x, $y, 0)), :pattern(' ') 98 | }, 99 | :text($idx), :name("L" ~ $idx); 100 | self.set-await: $c; #, $c.updated; 101 | } 102 | } 103 | self.await-redraws; 104 | $reporter.say: "DONE!"; 105 | } 106 | nextsame 107 | } 108 | 109 | multi method event(Event::ColorRotate:D $ev) { 110 | $!color-shift += π / 3; 111 | my $reporter = $.app.reporter; 112 | $reporter.say: "SHIFT: ", $!color-shift.fmt('%.4f'); 113 | my $now = now; 114 | if $!loop { 115 | $reporter.say: "LOOP: ", ($now - $!loop).fmt('%.2f'), " sec."; 116 | } 117 | $!loop = $now; 118 | self.flatten-block; 119 | self.flow: :name('SHIFT'), { 120 | self.invalidate; 121 | my @evp; 122 | self.children.race(:batch(8), :degree($*KERNEL.cpu-cores)).map: -> $c { 123 | if $c ~~ Vikna::Label { 124 | my $bg = self.l-color($c.x div $!lw, $c.y, $!color-shift); 125 | $c.set-color: :fg, :$bg; 126 | self.set-await: $c; #, $c.status-reset; 127 | $c.invalidate; 128 | $c.redraw; 129 | } 130 | } 131 | self.await-redraws; 132 | } 133 | $.app.reporter.say: "Shifted"; 134 | } 135 | 136 | multi method event(Vikna::Event::Flattened:D $ev) { 137 | if $!status-code == 2 { 138 | self.dispatch: Event::ColorRotate; 139 | $.app.reporter.say: "FLATTENED! "; 140 | $!status-code ⚛= 0; 141 | } 142 | } 143 | } 144 | 145 | class ThreadApp is Vikna::App { 146 | has $.reporter; 147 | method main(|) { 148 | constant INFO-W = 25; 149 | $!reporter = $.desktop.create-child: 150 | Vikna::TextScroll, StBack, 151 | x => $.desktop.w - INFO-W, 152 | y => 1, 153 | :w(INFO-W), :h($.desktop.h - 1), 154 | :name, 155 | :attr{ 156 | :pattern(' '), :bg(0x10, 0x10, 0x10), :fg; 157 | }; 158 | my $tt = $.desktop.create-child: Vikna::Label, 159 | :name, 160 | :x($.desktop.w - INFO-W), :y(0), :w(INFO-W), :h(1), 161 | :text('tick-tock'), 162 | :fg, :bg(0x80, 0x80, 0x80); 163 | $.desktop.create-child: 164 | Rainbow, 165 | :x(0), :y(0), 166 | :w($.desktop.w - 25), :h($.desktop.h), 167 | # :w(48), :h(8), 168 | ; 169 | for ^Inf -> $counter { 170 | sleep 1; 171 | $tt.set-text: "tick-tok " ~ $counter.fmt('%8d'); 172 | } 173 | } 174 | } 175 | 176 | my $tapp = ThreadApp.new(:!debugging); 177 | $tapp.run; 178 | -------------------------------------------------------------------------------- /examples/tscroll.raku: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | # use lib "%*ENV/src/Raku/Terminal-Print/lib"; 3 | # use lib "%*ENV/src/Raku/raku-Terminal-Window/lib"; 4 | use Vikna::App; 5 | use Vikna::Events; 6 | use Vikna::TextScroll; 7 | 8 | my $count = 1000; 9 | 10 | class MyScroll is Vikna::TextScroll { 11 | 12 | has $.st; 13 | has $.et; 14 | 15 | my class Event::Cmd::NextLine is Event::Command { 16 | # Uncommenting the following method will skyrocket the benchmark but would result in "batch-update" of the 17 | # widget. Only makes interest for experimental purposes. 18 | # method priority { PrioOut } 19 | } 20 | 21 | multi method event(Event::Attached:D $ev) { 22 | if $ev.child === self { 23 | $!st = now; 24 | $.next-line(0); 25 | # Send lines from a separate thread. 26 | # $.flow: :name('Async out'), { 27 | # for ^$count -> $i { 28 | # $.say: "app line ", $i.fmt(‘%4d’); 29 | # sleep .05; 30 | # } 31 | # }; 32 | } 33 | } 34 | 35 | method cmd-nextline(UInt:D $line) { 36 | my $do-next = True; 37 | given $line { 38 | when * < $count { 39 | my $c = 10.rand.Int; 40 | my $s = $c x $c; 41 | self.say: "Line [{$line.fmt: '%4d'}], {$.buffer.elems.fmt: '%4d'} in buf, $s"; 42 | } 43 | when $count { 44 | self.print: "Line A\c[FORM FEED]Line B"; 45 | } 46 | when ($count + 1) { 47 | self.print: "\rLB"; 48 | } 49 | when ($count + 2) { 50 | self.say: "\n+++"; 51 | } 52 | default { 53 | $do-next = False; 54 | $.app.desktop.close; 55 | $!et = now; 56 | } 57 | } 58 | $.next-line($line + 1) if $do-next; 59 | } 60 | 61 | method next-line(UInt:D $line) { 62 | self.send-command: Event::Cmd::NextLine, $line; 63 | } 64 | } 65 | 66 | class ScrollApp is Vikna::App { 67 | has $.ts is rw; 68 | method main { 69 | $!ts = $.desktop.create-child: MyScroll, :w(80), :h($.desktop.h - 5), :x(20), :y(3), :bg-pattern(' '), :auto-clear; 70 | } 71 | } 72 | 73 | my $app = ScrollApp.new: :!debugging; 74 | $app.run; 75 | note "Bench result: ", ($app.ts.et - $app.ts.st), " seconds, ", $count / ($app.ts.et - $app.ts.st), " lines/sec"; 76 | -------------------------------------------------------------------------------- /examples/z-order.raku: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | use Vikna::App; 3 | use Vikna::Desktop; 4 | use Vikna::Window; 5 | use Vikna::Events; 6 | use Vikna::Utils; 7 | use Vikna::TextScroll; 8 | use AttrX::Mooish; 9 | 10 | constant NUM-WIN = 10; 11 | 12 | class Event::ChangeTop is Vikna::Event { 13 | method default-priority { PrioCommand } 14 | }; 15 | 16 | class ZWin is Vikna::Window { 17 | has $!change-top = False; 18 | multi method event(Event::Focus::In:D $ev) { 19 | $!change-top = True; 20 | } 21 | multi method event(Event::Redrawn:D $ev) { 22 | if $!change-top { 23 | $.app.desktop.say: $.name, " redrawn focused"; 24 | $.app.desktop.dispatch: Event::ChangeTop; 25 | $!change-top = False; 26 | } 27 | nextsame 28 | } 29 | } 30 | 31 | class ZDesktop is Vikna::Desktop { 32 | has Vikna::TextScroll $.info; 33 | has $.counter = 20; 34 | has $.left-attach = NUM-WIN; 35 | has $!last-change; 36 | 37 | multi method event(Event::Init:D $ev) { 38 | for ^NUM-WIN { 39 | self.create-child: 40 | ZWin, 41 | :x($_ * 5), :y($_ * 2), :30w, :10h, 42 | :pattern(~$_), 43 | :name('Window' ~ $_), 44 | :title('Window #' ~ $_), 45 | ; 46 | } 47 | $!last-change = now; 48 | $!info = self.create-child: 49 | Vikna::TextScroll, StBack, 50 | :x($.w - 40), :y($.h - 20), :40w, :20h, 51 | :name, :pattern(' '); 52 | } 53 | 54 | multi method event(Event::ChangeTop:D $ev) { 55 | my $i; 56 | my $w; 57 | my $changed-at = now; 58 | repeat { 59 | $i = NUM-WIN.rand.floor; 60 | $w = self{'Window' ~ $i}; 61 | } while $w.in-focus; 62 | my $vf = $*VIKNA-FLOW; 63 | $.app.screen.availability_promise.then: { 64 | my $*VIKNA-FLOW = $vf; 65 | self.to-top: $w; 66 | if $!info { 67 | $!info.say: $!counter.fmt('%3d'), ". Window $i -> top; ", ($changed-at - $!last-change).fmt('%.4f'); 68 | $!last-change = $changed-at; 69 | } 70 | --$!counter; 71 | unless $!counter > 0 { 72 | self.quit 73 | } 74 | } 75 | } 76 | 77 | multi method event(Event::Attached:D $ev) { 78 | if $ev.parent === self && $ev.child ~~ ZWin { 79 | --$!left-attach; 80 | .say: $ev.child.name, " prepared" with $!info; 81 | } 82 | } 83 | } 84 | 85 | class ZApp is Vikna::App { 86 | method main { 87 | } 88 | } 89 | 90 | ZApp.new( :!debugging, :desktop-class(ZDesktop) ).run; 91 | -------------------------------------------------------------------------------- /lib/Vikna.rakumod: -------------------------------------------------------------------------------- 1 | use v6; 2 | unit module Vikna:ver<0.0.3>; 3 | -------------------------------------------------------------------------------- /lib/Vikna/App.rakumod: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | use Vikna::Object; 3 | unit class Vikna::App; 4 | also is Vikna::Object; 5 | 6 | use Terminal::Print; 7 | use Vikna::Widget; 8 | use Vikna::Desktop; 9 | use Vikna::Screen; 10 | use Vikna::Events; 11 | use Vikna::OS; 12 | use Vikna::Tracer; 13 | use AttrX::Mooish; 14 | 15 | my ::?CLASS $app; 16 | 17 | # Named parameters to be passed to a screen driver constructor 18 | has %.screen-profile; 19 | has Vikna::Screen $.screen is mooish(:lazy); 20 | has Vikna::Desktop $.desktop is mooish(:lazy); 21 | has Vikna::Tracer $.tracer is mooish(:lazy); 22 | # Tracer database name 23 | has Str $.tracer-name is mooish(:lazy); 24 | has Bool:D $.debugging = False; 25 | has Vikna::OS $.os is mooish(:lazy) handles ; 26 | # Named parameters for $.desktop constructor 27 | has %.desktop-profile; 28 | has Vikna::Desktop $.desktop-class; 29 | 30 | method new(|) { 31 | $app //= callsame; 32 | } 33 | 34 | submethod profile-default { 35 | desktop-profile => %( 36 | :name, 37 | attr => { 38 | :pattern<.>, 39 | }, 40 | :!auto-clear, 41 | # :bg, 42 | # :inv-mark-color<60,60,140>, 43 | ) 44 | } 45 | 46 | my %os2mod = 47 | darwin => 'unix', 48 | freebsd => 'unix', 49 | linux => 'unix'; 50 | 51 | method build-os { 52 | self.throw: X::OS::Unsupported, os => $*VM.osname 53 | unless %os2mod{$*VM.osname}:exists; 54 | 55 | my $os-module = 'Vikna::OS::' ~ %os2mod{$*VM.osname}; 56 | 57 | require ::($os-module); 58 | self.create: ::($os-module); 59 | } 60 | 61 | method build-screen { 62 | $.os.screen.init: |%!screen-profile; 63 | $.os.screen 64 | } 65 | 66 | method build-tracer-name { 67 | .subst(":", "_", :g) ~ ".sqlite" with self.^name; 68 | } 69 | 70 | method build-tracer { 71 | # note "CREATING TRACER DB ", $db-name, " with session ", self.^name; 72 | Vikna::Tracer.new: :db-name( $.tracer-name ), :session-name( self.^name ), :!to-err; 73 | } 74 | 75 | method build-desktop { 76 | self.create: 77 | $!desktop-class.WHAT, 78 | |%!desktop-profile, 79 | :geom($.screen.geom.clone), 80 | ; 81 | } 82 | 83 | proto method trace(|) { 84 | return unless $!debugging; 85 | {*} 86 | } 87 | multi method trace(&code, *%c) { 88 | self.trace: |&code(), |%c 89 | } 90 | multi method trace(*@args, :$obj = self, *%c) { 91 | my $message = @args.join; 92 | for { # predefined classes 93 | %c = $_ if %c{$_}:delete; 94 | } 95 | $!tracer.record(:object-id($obj.?name // ~$obj.WHICH), :$message, |%c) 96 | } 97 | 98 | multi method run(::?CLASS:U: |c) { 99 | self.new.run(|c); 100 | } 101 | 102 | multi method run(::?CLASS:D: |c) { 103 | self.flow: :sync, :name('MAIN'), { 104 | PROCESS::<$VIKNA-APP> = self; 105 | self.trace: "Starting app" ~ self.^name, obj => self, :phase; 106 | $!desktop.dispatch: Event::Init; 107 | $!desktop.dispatch: Event::Focus::In; 108 | $!desktop.invalidate; 109 | $!desktop.redraw; 110 | $!desktop.sync-events: :transitive; 111 | self.trace: "PASSING TO MAIN", :phase; 112 | $.main(|c); 113 | self.trace: "MAIN IS DONE", :phase; 114 | $.desktop.sync-events(:transitive) unless $.desktop.closed; 115 | self.trace: "CLOSING DESKTOP", :phase; 116 | await $.desktop.dismissed; 117 | self.trace: "APP DONE!", :phase; 118 | 119 | LEAVE { 120 | # $!screen.shutdown; 121 | $!tracer.shutdown if $!debugging; 122 | } 123 | CATCH { 124 | default { 125 | note .message, ~.backtrace; 126 | self.trace: .message, .backtrace, :error; 127 | .rethrow; 128 | } 129 | } 130 | } 131 | } 132 | 133 | method create(Mu \type, |c) { 134 | type.new( :app(self), |c ); 135 | } 136 | 137 | my $panic-lock = Lock.new; 138 | method panic($cause, :$object?) { 139 | $panic-lock.protect: { 140 | CATCH { 141 | default { 142 | note "APP PANIC PANICED: ", .message, ~.backtrace; 143 | exit 2; 144 | } 145 | } 146 | my $obj-id = $object.?name // $object.WHICH; 147 | my $msg = "Caused by {$obj-id}\n" ~ $cause ~ $cause.backtrace 148 | ~ "\nPanic path:\n" ~ Backtrace.new; 149 | self.trace: "APP PANIC! ", $msg, :error; 150 | note "===APP PANIC!=== ", $msg; 151 | $.desktop.panic-shutdown($cause); 152 | $!tracer.shutdown if $!debugging; 153 | exit 1; 154 | } 155 | } 156 | 157 | ### Utility methods ### 158 | 159 | method profile-config(\type, $name?) { 160 | %() 161 | } 162 | -------------------------------------------------------------------------------- /lib/Vikna/Border.rakumod: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | 3 | unit class Vikna::Border; 4 | 5 | use Vikna::Widget::GroupMember; 6 | use Vikna::Events; 7 | 8 | also is Vikna::Widget::GroupMember; 9 | 10 | my %borders = 11 | ascii => %( 12 | passive => %( 13 | :ul<+>, :t<->, :ur<+>, 14 | :l<|>, :r<|>, 15 | :bl<+>, :b<->, :br<+>, 16 | # Connectors 17 | :cl<+>, :cr<+>, :ct<+>, :cb<+>, 18 | ), 19 | active => %( 20 | :ul<+>, :t<=>, :ur<+>, 21 | :l<|>, :r<|>, 22 | :bl<+>, :b<->, :br<+>, 23 | # Connectors 24 | :cl<+>, :cr<+>, :ct<+>, :cb<+>, 25 | ), 26 | ), 27 | ; 28 | has $.type = 'ascii'; 29 | 30 | ### Event handlers 31 | 32 | # Set own 'event horizon' 33 | proto method event(Event:D $) {*} 34 | 35 | multi method event(Event::Attached:D $ev) { 36 | if $ev.child === self { 37 | self.subscribe: $.parent, -> $pev { 38 | if $pev ~~ Event::Focus::In | Event::Focus::Out { 39 | self.trace: "Parent focus in/out event, redraw self"; 40 | self.invalidate; 41 | self.redraw; 42 | } 43 | } 44 | } 45 | nextsame 46 | } 47 | 48 | multi method event(Event:D $) { nextsame } 49 | 50 | ### Command senders ### 51 | 52 | # Prevent voluntary geom changes 53 | method set-geom(|) { } 54 | 55 | ### Utility methods ### 56 | 57 | method draw( :$canvas ) { 58 | my %b = %borders{ $!type }{ $.group.in-focus ?? 'active' !! 'passive' }; 59 | self.trace: "### BORDER DRAW, group focused: ", ?$.group.in-focus; 60 | my $r = $.w - 1; 61 | my $b = $.h - 1; 62 | $canvas.imprint(0, 0, %b
    ); 63 | $canvas.imprint($r, 0, %b); 64 | $canvas.imprint(0, $b, %b); 65 | $canvas.imprint($r, $b, %b
    ); 66 | my $bottom = %b x ($.w - 2); 67 | $canvas.imprint(1, $b, $bottom); 68 | # Limit title length. 69 | my $top; 70 | if $.parent.?title && $.w > 6 { 71 | my $title = " " ~ $.parent.title.substr(0, $.w - 6) ~ " "; 72 | self.trace: "Using title ‘$title’"; 73 | my $tlen = $.w - 2 - $title.chars; 74 | my $l-len = $tlen div 2; 75 | my $r-len = $tlen - $l-len; 76 | $top = (%b x $l-len) ~ $title ~ (%b x $r-len); 77 | } 78 | else { 79 | $top = %b x $.w - 2; 80 | } 81 | $canvas.imprint(1, 0, $top); 82 | for 1..($.h-2) -> $y { 83 | $canvas.imprint(0, $y, %b); 84 | $canvas.imprint($r, $y, %b); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /lib/Vikna/Button.rakumod: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | 3 | unit class Vikna::Button; 4 | 5 | use Vikna::Events; 6 | use Vikna::Focusable; 7 | use Vikna::PointerTarget; 8 | use Vikna::Widget; 9 | use Vikna::Utils; 10 | use AttrX::Mooish; 11 | 12 | also is Vikna::Focusable; 13 | also does Vikna::PointerTarget; 14 | 15 | has Bool $.is-unicode is mooish(:lazy); 16 | has Bool $.use3d is mooish(:lazy); 17 | has Str:D $.text is required; 18 | has $.shadow-color; 19 | 20 | # If defined, $.event would be sent to this target widget. 21 | has Vikna::Widget $.target is rw; 22 | # Could be an instance too. 23 | has Event::Button $.event = Event::Button::Press; 24 | 25 | has Bool:D $!pressed = False; 26 | 27 | ### Utility methods ### 28 | 29 | submethod profile-default { 30 | :h(1), :text, :!auto-clear, 31 | attr => { :fg, :bg('magenta'), :pattern(' ') }, 32 | focused-attr => { :fg, :bg, :pattern(' ') }, 33 | :shadow-color 34 | } 35 | 36 | submethod profile-checkin(%profile, %constructor, %, %) { 37 | # .trace: "Button profile for checkin: ", %profile with %profile; 38 | my %geom-profile; 39 | my $use3d = %profile; 40 | %geom-profile = %profile.chars + ($use3d ?? 3 !! 4) unless %constructor; 41 | unless %constructor { 42 | %geom-profile = $use3d ?? 2 !! 1 43 | } 44 | if %geom-profile { 45 | %profile = %profile.dup: |%geom-profile; 46 | } 47 | # .trace: "Updated profile for ", self.^name, "::new\n", %profile.map({ .key ~ " => " ~ (.value ~~ Vikna::Object ?? .value.WHICH !! .value.raku) }).join("\n") 48 | # with %profile; 49 | } 50 | 51 | method build-is-unicode { 52 | $.app.screen.is-unicode 53 | } 54 | 55 | method build-use3d { 56 | $.h > 1 57 | } 58 | 59 | ### Event handlers ### 60 | 61 | multi method event(Event::Mouse::Press:D $ev) { 62 | nextsame unless $ev.button == MBLeft; 63 | unless $.in-focus { 64 | $.focus; 65 | } 66 | self!set-pressed(True); 67 | } 68 | 69 | multi method event(Event::Mouse::Release:D $ev) { 70 | nextsame unless $ev.button == MBLeft; 71 | self!set-pressed(False, :report) 72 | } 73 | 74 | # multi method event(Event::Mouse::Enter:D $ev) { 75 | # nextsame unless $ev.buttons[MBLeft]; 76 | # self!set-pressed(True); 77 | # } 78 | 79 | multi method event(Event::Mouse::Leave:D $ev) { 80 | nextsame unless $ev.buttons[MBLeft]; 81 | self!set-pressed(False); 82 | } 83 | 84 | multi method event(Event::Kbd:D $ev) { 85 | if $ev.char eq ' ' { 86 | self!set-pressed(True); 87 | self!set-pressed(False, :report); 88 | } 89 | } 90 | 91 | ### Utility methods ### 92 | 93 | method !set-pressed($pressed, :$report?) { 94 | if $!pressed xor $pressed { 95 | self.trace: "pressed state change from $!pressed into $pressed"; 96 | $!pressed = ? $pressed; 97 | self.dispatch: $!pressed ?? Event::Button::Down !! Event::Button::Up; 98 | if $report && $!target && ! $!pressed { 99 | self.dispatch: Event::Button::Press; 100 | if $!event.defined { 101 | $!target.dispatch: $!event.dup(:origin(self)); 102 | } 103 | else { 104 | $!target.dispatch: $!event, :origin(self) 105 | } 106 | } 107 | $.cmd-redraw(:force); 108 | } 109 | } 110 | 111 | # method !still-pressed { 112 | # # XXX If any other pointers get support they must be handled here too. 113 | # $!pressed &&= $.parent.is-pointer-owner('mouse', self); 114 | # } 115 | 116 | method draw3d(:$canvas) { 117 | self.trace: "3D button\n - size: ", $.geom, "\n - viewport: ", $.viewport, "\n - pressed: ", $!pressed; 118 | my $bw = $.w - 1; 119 | # $canvas.invalidate: $.invalidate; 120 | $canvas.clear; 121 | $canvas.invalidate: self.invalidate: 0, 0, $bw, 1; 122 | $canvas.invalidate: self.invalidate: 1, 1, $bw, 1; 123 | my $outtext = $!text.substr(0, $bw); 124 | my $y = $!pressed ?? 1 !! 0; 125 | my $x = $y + (($bw - $outtext.chars) / 2).truncate; 126 | unless $!pressed { 127 | self.trace: "Unpressed button"; 128 | $canvas.imprint: 1, 1, ' ' x $bw, bg => $!shadow-color, fg => $!shadow-color; 129 | } 130 | self.trace: "Imprinting ‘$outtext’ into $x, $y"; 131 | $canvas.imprint: $y, $y, $.attr.pattern x $bw, bg => $.attr.bg, fg => $.attr.fg; 132 | $canvas.imprint: $x, $y, $outtext, bg => $.attr.bg, fg => $.attr.fg; 133 | } 134 | 135 | method draw2d(:$canvas) { 136 | $.invalidate; 137 | self.draw-background(:$canvas); 138 | my @braces = $!pressed ?? «( )» !! «< >»; 139 | my $btext = @braces[0] ~ " " ~ $!text ~ " " ~ @braces[1]; 140 | my $y = (($.h - 1) / 2).truncate; 141 | my $x = (($.w - $btext.chars) / 2).truncate; 142 | $canvas.imprint: $x, $y, $btext, :$.fg, bg => $.bg ~ ($!pressed ?? " bold" !! ""); 143 | } 144 | 145 | method draw(:$canvas) { 146 | # self!still-pressed; 147 | if $!use3d { 148 | self.draw3d: :$canvas 149 | } 150 | else { 151 | self.draw2d: :$canvas 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /lib/Vikna/CAttr.rakumod: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | 3 | =begin pod 4 | =NAME 5 | 6 | C - on screen symbol attributes 7 | 8 | =DESCRIPTION 9 | 10 | Class defines basic attributes of a symbol on screen: it's foreground and background colors, and style. 11 | 12 | =ATTRIBUTES 13 | 14 | =head3 C<$.fg> 15 | 16 | Foreground color 17 | 18 | =head3 C<$.bg> 19 | 20 | Bacground color 21 | 22 | =head3 C 23 | 24 | Style of the symbol. See C constants in L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Utils.md>. 25 | 26 | =head3 C<%.Profile> 27 | 28 | Cached representation of the attribute suitable for passing into methods like 29 | L|https://github.com/vrurg/raku-Vikna/blob/v0.0.3/docs/md/Vikna/Canvas.md>C<::imprint>. 30 | 31 | =METHODS 32 | 33 | =head3 C 34 | =head3 C 35 | =head3 C 36 | 37 | All three methods preserve their usual meaning with one nuance: if C 60 | 61 | 62 | 69 | 70 | 71 | {{#session}} 72 | 73 | 74 | 75 | 76 | 77 | 78 | {{#flows}} 79 | 80 | {{/flows}} 81 | 82 | {{#rows}} 83 | {{#record}} 84 | 85 | 86 | 87 | {{#cols}} 88 | {{#cell}} 89 | 96 | {{/cell}} 97 | {{^cell}} 98 | 99 | {{/cell}} 100 | {{/cols}} 101 | 102 | {{/record}} 103 | {{#flow_name}} 104 | 105 | 106 | 107 | {{#cols}} 108 | {{#cell}} 109 | 110 | {{/cell}} 111 | {{^cell}} 112 | {{/cell}} 113 | 114 | {{/cols}} 115 | 116 | {{/flow_name}} 117 | {{/rows}} 118 | {{/session}} 119 | 120 |
    Session {{name}} #{{id}}
    IDTimeFlow #{{flow}}
    {{id}}{{time}} 90 |
    {{flow_name}}
    91 |
    {{object_id}}
    92 | {{#message}} 93 |
    {{line}}
    94 | {{/message}} 95 |
    {{id}}{{time}}{{flow_name}}
    121 | 122 | 123 | -------------------------------------------------------------------------------- /resources/tracer/txt.tmpl: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vrurg/raku-Vikna/9604ecc89320f1bd0eee539095c6cfa6f6d7bbe8/resources/tracer/txt.tmpl -------------------------------------------------------------------------------- /t/009-parent-child.t: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | use Test::Async; 3 | 4 | use Vikna::Parent; 5 | use Vikna::Child; 6 | use Vikna::Utils; 7 | use Vikna::X; 8 | 9 | plan 3, :parallel, :random; 10 | 11 | class Child does Vikna::Child is Vikna::Object { 12 | has $.data; 13 | } 14 | 15 | class Parent does Vikna::Parent[Child] { 16 | } 17 | 18 | subtest "Basics" => { 19 | plan 8; 20 | my Child $c .= new; 21 | my Parent $p .= new; 22 | 23 | ok ($p.add-child: $c), "add-child returns truish value on success"; 24 | nok ($p.add-child: $c), "add-child return falsish value for existing child"; 25 | is $p.elems, 1, "double-add doesn't duplicate"; 26 | 27 | for ^9 { 28 | $p.add-child: Child.new; 29 | } 30 | is $p.elems, 10, "plus 9 children makes it 10"; 31 | $p.remove-child: $p.children[3]; 32 | is $p.elems, 9, "a child has been removed"; 33 | 34 | $p.to-top($c = $p.children[2]); 35 | is $p.children.tail, $c, "child moved to top"; 36 | 37 | $p.to-bottom($c = $p.children[2]); 38 | is $p.children.head, $c, "child moved to bottom"; 39 | 40 | is-deeply $p.children.map( *.parent ).list, ($p xx 9), "all parents are set"; 41 | } 42 | 43 | subtest "Strata" => { 44 | plan 32; 45 | my Child $c .= new; 46 | my Parent $p .= new; 47 | 48 | $p.add-child: $c; 49 | is $p.child-stratum($c), StMain, "by default we add into main stratum"; 50 | $p.add-child: $c, :stratum(StBack); 51 | is $p.elems, 1, "double-add doesn't duplicate"; 52 | is $p.child-stratum($c), StMain, "duplicate insert doesn't change child stratum"; 53 | $p.remove-child($c); 54 | is $p.elems, 0, "child removed"; 55 | 56 | my $i = 0; 57 | for StMain, StModal, StBack -> $st { 58 | for ^3 { 59 | $p.add-child: Child.new(data => $i++), :stratum($st) 60 | } 61 | } 62 | is $p.elems, 9, 'all children added to strata'; 63 | isa-ok $p.children, List, "children method returns a List"; 64 | is-deeply $p.children.map( *.data ).list, (6, 7, 8, 0, 1, 2, 3, 4, 5), "children are sorted by stratum"; 65 | is-deeply $p.children(:reverse).map( *.data ).list, (5, 4, 3, 2, 1, 0, 8, 7, 6), "children are reverse-sorted by stratum"; 66 | 67 | isa-ok $p.children(:lazy), Seq, "lazy children returns a Seq"; 68 | ok $p.children(:lazy).is-lazy, "lazy children Seq is lazy"; 69 | is-deeply $p.children(:lazy).map( *.data ).list.eager, (6, 7, 8, 0, 1, 2, 3, 4, 5), "lazy children are sorted by stratum"; 70 | is-deeply $p.children(:reverse, :lazy).map( *.data ).list.eager, (5, 4, 3, 2, 1, 0, 8, 7, 6), "lazy children are reverse-sorted by stratum"; 71 | is-deeply $p.children(StModal, :lazy).map( *.data ).list.eager, (3, 4, 5), "lazy children iterates over a stratum"; 72 | is-deeply $p.children(StModal, :lazy, :reverse).map( *.data ).list.eager, (5, 4, 3), "lazy children reverse-iterates over a stratum"; 73 | 74 | is-deeply $p.children(StBack).map( *.data ).list, (6, 7, 8), "StBack stratum children"; 75 | is $p.elems(StBack), 3, "StBack stratum elems"; 76 | is-deeply $p.children(StMain).map( *.data ).list, (0, 1, 2), "StMain stratum children"; 77 | is $p.elems(StMain), 3, "StMain stratum elems"; 78 | is-deeply $p.children(StModal).map( *.data ).list, (3, 4, 5), "StModal stratum children"; 79 | is $p.elems(StModal), 3, "StModal stratum elems"; 80 | $p.remove-child($p.children[1]); 81 | is-deeply $p.children.map( *.data ).list, (6, 8, 0, 1, 2, 3, 4, 5), "children are sorted by stratum"; 82 | is-deeply $p.children(StBack).map( *.data ).list, (6, 8), "StBack stratum after child removal"; 83 | is $p.elems(StBack), 2, "StBack elems after child removal"; 84 | is $p.elems(StMain), 3, "StMain number of children didn't change"; 85 | is $p.elems(StModal), 3, "StModal number of children didn't change"; 86 | throws-like { $p.remove-child(Child.new) }, X::NoChild, "removal of non-existing child throws"; 87 | 88 | $p.to-top($p.children[3]); 89 | is-deeply $p.children(StMain).map( *.data).list, (0, 2, 1), "child moving to top inside its stratum"; 90 | is-deeply $p.children.map( *.data).list, (6, 8, 0, 2, 1, 3, 4, 5), "child order after to-top operation"; 91 | 92 | $p.to-bottom($p.children[6]); 93 | is-deeply $p.children(StModal).map( *.data).list, (4, 3, 5), "child moving to bottom inside its stratum"; 94 | is-deeply $p.children.map( *.data).list, (6, 8, 0, 2, 1, 4, 3, 5), "child order after to-bottom operation"; 95 | 96 | $c = Child.new(data => pi); 97 | $p.add-child($c, :stratum(StBack)); 98 | ok $p.is-topmost($c), "last added child is the topmost one is its stratum"; 99 | ok $p.is-bottommost($p.children[6]), "moved to bottom child is the bottommost one in its stratum"; 100 | } 101 | 102 | subtest "Locking" => { 103 | plan 2; 104 | my $sync1 = Promise.new; 105 | my $sync2 = Promise.new; 106 | my Parent $p .= new; 107 | 108 | for ^100 { 109 | $p.add-child(Child.new(data => $_)); 110 | } 111 | 112 | my $loop1-end; 113 | my $loop2-start; 114 | my @order; 115 | my @w = start { 116 | @order.push: "b1"; 117 | $p.for-children: { 118 | if $sync1.status ~~ Planned { 119 | $sync1.keep(True); 120 | await $sync2; 121 | @order.push: "c1"; 122 | } 123 | } 124 | @order.push: "e1"; 125 | $loop1-end = now; 126 | }, 127 | start { 128 | await $sync1; # Make sure the first loop started 129 | @order.push: "b2"; 130 | $sync2.keep(True); 131 | $p.for-children: { 132 | $loop2-start = now unless $loop2-start; 133 | } 134 | @order.push: "e2"; 135 | }; 136 | 137 | await @w; 138 | 139 | ok $loop2-start > $loop1-end, "for-children loops are never running simultaneously"; 140 | is-deeply @order, [|], "order of events is persistent"; 141 | } 142 | -------------------------------------------------------------------------------- /t/011-events.t: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | use Test::Async; 3 | use Vikna::EventHandling; 4 | use Vikna::Events; 5 | use Vikna::Object; 6 | use Vikna::Utils; 7 | 8 | plan 3; 9 | 10 | class EvTest is Vikna::Event { 11 | has $.num; 12 | 13 | my $counter = 0; 14 | 15 | submethod TWEAK(*%c) { 16 | $!num = $counter++ unless %c:exists; 17 | } 18 | 19 | submethod reset { $counter = 0 } 20 | } 21 | class EvDone is Vikna::Event { } 22 | 23 | class EvObj is Vikna::Object does Vikna::EventHandling { 24 | has Promise:D $.dn .= new; 25 | has @.counts is rw; 26 | 27 | multi method event(EvTest $ev) { 28 | @.counts.push: $ev.num 29 | } 30 | multi method event(EvDone $ev) { 31 | $!dn.keep(True); 32 | } 33 | } 34 | 35 | sub await-tout(+@p) { 36 | await Promise.anyof: Promise.in(10), |@p 37 | } 38 | 39 | subtest "Basic dispatching" => { 40 | plan 1; 41 | 42 | my $inst = EvObj.new; 43 | for ^10 { 44 | $inst.dispatch: EvTest, num => $_ 45 | } 46 | $inst.dispatch: EvDone; 47 | await-tout $inst.dn; 48 | 49 | is-deeply $inst.counts, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "all events received and are in order"; 50 | } 51 | 52 | subtest "Subscriptions" => { 53 | plan 3; 54 | my @inst; 55 | for ^2 { 56 | @inst.push: EvObj.new; 57 | } 58 | 59 | my class EvObjU is EvObj { 60 | multi method subscription-event(EvTest $ev) { 61 | self.event: $ev; 62 | if $ev.num == 4 { 63 | self.unsubscribe(@inst[0]); 64 | } 65 | } 66 | } 67 | 68 | @inst[2] = EvObjU.new; 69 | 70 | # Test subscribe with code object 71 | @inst[1].subscribe(@inst[0], { @inst[1].event: $_ } ); 72 | # Default will dispatch to subscriptio-event 73 | @inst[2].subscribe(@inst[0]); 74 | 75 | for ^10 { 76 | @inst[0].dispatch: EvTest, num => $_; 77 | } 78 | @inst[0].dispatch: EvDone; 79 | await-tout |@inst[0,1].map: { .dn }; 80 | 81 | is-deeply @inst[0].counts, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "all events received and are in order for the initial object"; 82 | is-deeply @inst[1].counts, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], "all events received and are in order for the first subscriber"; 83 | is-deeply @inst[2].counts, [0, 1, 2, 3, 4], "unsubscribing cut off last event for the second subscriber"; 84 | } 85 | 86 | subtest "Tracing" => { 87 | plan 2; 88 | 89 | my class Event::Trace::First is Event {} 90 | my class Event::Trace::Second is Event {} 91 | my class Event::Trace::Third is Event {} 92 | my class Event::Trace::None is Event {} 93 | 94 | my class EvObjTrace is EvObj { 95 | has Promise:D $.done .= new; 96 | has $.tracer = "test-tracing"; 97 | has $.suite; 98 | 99 | method start-trace { 100 | $!done .= new; 101 | $!tracer = 42; 102 | tag-event 42 => { 103 | self.dispatch: Event::Trace::First; 104 | } 105 | } 106 | 107 | multi method event(Event::Trace::First:D $ev) { 108 | $!suite.ok: ($!tracer ∈ $ev.tags), "first event has the tag set"; 109 | tag-event "second", { 110 | self.dispatch: Event::Trace::Second; 111 | } 112 | } 113 | multi method event(Event::Trace::Second:D $ev) { 114 | $!suite.ok: ($!tracer ∈ $ev.tags), "second event get its tracer tag from the first one"; 115 | $!suite.ok: ("second" ∈ $ev.tags), "second event got its second tag from tag-event"; 116 | # Thread.start is the only way to guarantee cut off of dynamic context 117 | Thread.start: flow-branch { 118 | self.dispatch: Event::Trace::Third; 119 | } 120 | } 121 | multi method event(Event::Trace::Third:D $ev) { 122 | $!suite.ok: ($!tracer ∈ $ev.tags), "event dispatched within in-context flow inherits the trace tag"; 123 | $!suite.ok: ("second" ∈ $ev.tags), "event dispatched within in-context flow inherits the second tag"; 124 | Thread.start: { 125 | self.dispatch: Event::Trace::None; 126 | } 127 | } 128 | multi method event(Event::Trace::None:D $ev) { 129 | $!suite.nok: $ev.tags, "event dispatched within plain flow doesn't get tags"; 130 | $!done.keep; 131 | } 132 | } 133 | 134 | subtest "seed with tag-event" => -> $suite { 135 | plan 6; 136 | my $obj = EvObjTrace.new: :$suite; 137 | $obj.dispatch: Event::Trace::First, :tags(set $obj.tracer); 138 | await-tout $obj.done; 139 | } 140 | subtest "seed with tag-event" => -> $suite { 141 | plan 6; 142 | my $obj = EvObjTrace.new: :$suite; 143 | $obj.start-trace; 144 | await-tout $obj.done; 145 | } 146 | } 147 | 148 | done-testing; 149 | -------------------------------------------------------------------------------- /t/030-tracer.t: -------------------------------------------------------------------------------- 1 | use v6; 2 | # use Test::Async; 3 | use Test::Async; 4 | use Vikna::Tracer; 5 | use Vikna::Object; 6 | 7 | plan 8; 8 | 9 | my $obj = Vikna::Object.new; 10 | 11 | # Tracer can only run inside a flow. 12 | $obj.flow: :sync, :name('TEST TRACER'), { 13 | my $test-db = $*SPEC.catfile($*SPEC.tmpdir, "Test.sqlite"); 14 | sub wipe-db { 15 | if $test-db.IO.e { 16 | $test-db.IO.unlink; 17 | } 18 | } 19 | wipe-db; 20 | 21 | # diag "TDB: " ~ $test-db; 22 | 23 | my $tr; 24 | lives-ok { 25 | $tr = $obj.create: Vikna::Tracer, :db-name($test-db), :session-name('Test 1'); 26 | }, "tracer created"; 27 | 28 | for ^10 { 29 | $tr.record($tr, "test 1 line " ~ $_); 30 | } 31 | 32 | is $tr.session-id, 1, "first session id"; 33 | 34 | $tr.session-name = "Test 2"; 35 | 36 | for ^10 { 37 | $tr.record($tr, "test 2 line " ~ $_); 38 | } 39 | 40 | is $tr.session-id, 2, "second session id"; 41 | 42 | is $tr.sessions.elems, 2, "have two sessions"; 43 | is $tr.sessions[0].name, "Test 1", "first session name"; 44 | is $tr.sessions[1].name, "Test 2", "second session name"; 45 | 46 | for $tr.sessions -> $sess { 47 | # diag "SESS: " ~ $sess.id; 48 | subtest "Session " ~ $sess.id => { 49 | plan 10; 50 | my $prev-time = DateTime.new: 0; 51 | my $id = 0; 52 | for $sess.records -> $rec { 53 | subtest "Record " ~ $id => { 54 | plan 3; 55 | # diag join ", ", $rec.session-id ~ "#" ~ $rec.id, ~$rec.time, " ", $rec.object-id, " ", $rec.message; 56 | is $rec.message, "test " ~ $sess.id ~ " line " ~ $id, "$id. record message"; 57 | is $rec.id, ++$id, "record id"; 58 | ok $rec.time > $prev-time, "record time is sequential"; 59 | $prev-time = $rec.time; 60 | } 61 | } 62 | 63 | } 64 | } 65 | 66 | wipe-db; 67 | } 68 | 69 | done-testing; 70 | -------------------------------------------------------------------------------- /t/040-color.t: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | use Test::Async; 3 | use Vikna::Color; 4 | use Vikna::Color::Index; 5 | use Vikna::Color::Named; 6 | use Vikna::Color::RGB; 7 | use Vikna::Color::RGBA; 8 | 9 | plan 2, :parallel; 10 | 11 | diag "This suite does benchmarking, this might take a while." if $*OUT.t; 12 | 13 | subtest "Parsing" => { 14 | my @clist = 15 | # RGB of this line depends on the content of resources/color-index.json 16 | { color => '123', :valid, rgb => (135, 255, 255), type => 'Index', str => "123" }, 17 | { color => '300', :!valid }, 18 | { color => '#abc', :valid, rgb => (0xAA, 0xBB, 0xCC), type => 'RGB' }, 19 | { color => '#aabbcc', :valid, rgb => (0xAA, 0xBB, 0xCC), type => 'RGB' }, 20 | { color => '#aabbccdd', :valid, rgb => (0xAA, 0xBB, 0xCC, 0xDD), type => 'RGBA' }, 21 | { color => '#aabbcecdd', :!valid }, 22 | { color => '#aabccdd', :!valid }, 23 | { color => "green", :valid, rgb => (0, 255, 0), type => 'Named' }, 24 | { color => "nosuchcolor", :!valid }, 25 | { color => "1color", :!valid }, 26 | { color => "darkmagenta", :valid, rgb => (139, 0, 139), type => 'Named' }, 27 | { color => 'rgb:200,12,3', :valid, rgb => (200, 12, 3), type => 'RGB' }, 28 | { color => 'rgb:400,12,3', :!valid }, 29 | { color => 'rgb: .2, .5, .1', :valid, rgb => (51, 128, 26), type => 'RGB' }, 30 | { color => 'rgb: .2, 1.0, .1', :valid, rgb => (51, 255, 26), type => 'RGB' }, 31 | { color => 'rgba: .2, 1.0, .1, .6', :valid, rgb => (51, 255, 26, 153), type => 'RGBA' }, 32 | { color => 'rgba: 100, 150, 200, 128', :valid, rgb => (100, 150, 200, 128), type => 'RGBA' }, 33 | { color => 'rgbd: 1., .3, .1', :valid, rgb => (255, 77, 26), type => 'RGB' }, 34 | { color => 'rgbd: 100, 100, 100', :!valid }, 35 | { color => 'rgb: 100, 100, 100, 100', :!valid }, 36 | { color => 'rgba: 100, 100, 100, 100, 100', :!valid }, 37 | { color => 'rgb: 1, 1., 1', :!valid }, 38 | { color => 'rgb: 1., .3, 1', :!valid }, 39 | { color => '255,120,100', :valid, rgb => (255, 120, 100), type => 'RGB' }, 40 | { color => '.5, .3 , .8', :valid, rgb => (128, 77, 204), type => 'RGB' }, 41 | { color => '.5, 3, .8', :!valid }, 42 | { color => '100,100,0', :valid, rgb => (100, 100, 0), type => 'RGB' }, 43 | { color => "", :!valid }, 44 | { color => "12,13,14 underline", :!valid }, 45 | ; 46 | 47 | plan +@clist, :parallel, :random; 48 | 49 | for @clist -> %ctest { 50 | subtest 'Color string: "' ~ %ctest ~ '"' => { 51 | my $c = Vikna::Color.parse(%ctest); 52 | if %ctest { 53 | plan 4; 54 | ok ?$c, "color string is valid"; 55 | skip-rest "color string failed to parse" unless $c; 56 | my \expected-role = ::("Vikna::Color::" ~ %ctest); 57 | # diag "EXPECTED ROLE: " ~ expected-role.^name ~ ", color: " ~ $c.WHICH ~ ", matches: " ~ ($c ~~ expected-role); 58 | my $method = %ctest eq 'RGBA' ?? 'rgba' !! 'rgb'; 59 | if $c { 60 | does-ok $c, expected-role, "color type is " ~ %ctest; 61 | is-deeply $c."$method"().List, %ctest, "$method triplet"; 62 | my $expected-str = %ctest ~~ 'RGB' | 'RGBA' 63 | ?? %ctest.join(",") 64 | !! %ctest; 65 | is ~$c, $expected-str, "parsed color stringifies into '$expected-str'"; 66 | } 67 | } 68 | else { 69 | plan 1; 70 | nok ?$c, "color string is invalid"; 71 | } 72 | } 73 | } 74 | } 75 | 76 | subtest "Cache performance" => { 77 | plan 1; 78 | my @clist = ("rgb:" ~ ((255.rand).round xx 3).join(",")) xx 100; 79 | 80 | my $parse-loops = 300; 81 | my @pready = Promise.new xx 2; 82 | my $pstart = Promise.new; 83 | my @total = 0 xx 2; 84 | my @params = (), (:no-cache); 85 | 86 | my @p; 87 | for ^2 -> $bidx { 88 | @p.push: start { 89 | my %params = @params[$bidx]; 90 | @pready[$bidx].keep(True); 91 | await $pstart; 92 | for ^$parse-loops { 93 | for @clist -> $cstr { 94 | my $st = now; 95 | my $c = Vikna::Color.parse: $cstr, |%params; 96 | @total[$bidx] += now - $st; 97 | } 98 | } 99 | } 100 | } 101 | 102 | await @pready; 103 | $pstart.keep(True); 104 | 105 | await @p; 106 | 107 | if $*OUT.t { 108 | diag "Cached parse took " ~ @total[0].fmt("%.2f") ~ " sec"; 109 | diag "Uncached parse took " ~ @total[1].fmt("%.2f") ~ " sec"; 110 | } 111 | ok @total[0] < @total[1], "cached parsing is faster than non-cached by factor " ~ (@total[1] / @total[0]).fmt("%.2f"); 112 | } 113 | -------------------------------------------------------------------------------- /t/050-style-bits.t: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | use Test::Async; 3 | use Vikna::Utils; 4 | use Vikna::X; 5 | 6 | plan 12; 7 | 8 | is to-style(VSUnderline), VSTransparent, "no VSBase results in transparent style"; 9 | is to-style(VSBase +| VSItalic), VSBase +| VSItalic, "non-transparent style when VSBase is used"; 10 | is to-style([VSUnderline]), VSBase +| VSUnderline, "passing an array of effects results in non-transparent style"; 11 | is to-style((VSUnderline,)), VSBase +| VSUnderline, "passing a list of effects results in non-transparent style"; 12 | is to-style('underline bold'), VSBase +| VSBold +| VSUnderline, "string represenation is parsed correctly"; 13 | is to-style(''), VSTransparent, "empty string represents transparent style"; 14 | is to-style(' '), VSBase, "single space is no style"; 15 | is to-style('!'), VSBase +| VSBold, "single character is translated into a style constant"; 16 | is to-style(3.chr), VSTransparent, "chars below code is below 0x20 results in transparent style"; 17 | is to-style(0x43.chr), VSTransparent, "chars with codes where VSBase bit is not set result in transparent styles"; 18 | is to-style(Int), VSTransparent, "type object results in transparent style"; 19 | fails-like 20 | { to-style("semi bold style") }, 21 | X::CAttr::UnknownStyle, 22 | "bad style name causes expected failure", 23 | :message(q), 24 | :style('semi'); 25 | 26 | done-testing; 27 | -------------------------------------------------------------------------------- /t/060-test-app.t: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | 3 | use Test::Async ; 4 | use Vikna::Test::App; 5 | use Vikna::Desktop; 6 | use Vikna::Events; 7 | 8 | plan 4; 9 | 10 | class MyDesktop is Vikna::Desktop { 11 | has $!drawn = False; 12 | has $!test-first-draw = True; 13 | method draw { 14 | callsame; 15 | pass "Drawn for the first time" unless $!drawn; 16 | $!drawn = True; 17 | } 18 | 19 | multi method event(Event::Screen::Ready:D $ev) { 20 | if $!test-first-draw && $!drawn { 21 | $!test-first-draw = False; 22 | pass "Printed to the screen"; 23 | my $screen = $.app.screen; 24 | is-rect-filled 25 | $screen.buffer, 26 | $.app.screen.geom, 27 | "desktop filled the screen", 28 | :char($.app.desktop.attr.pattern); 29 | $.app.desktop.quit; 30 | } 31 | nextsame; 32 | } 33 | } 34 | 35 | class MyTestApp is Vikna::Test::App { 36 | method build-desktop { 37 | self.create: MyDesktop, |%.desktop-profile, :geom($.screen.geom.clone); 38 | } 39 | method main(|) { 40 | $.self-diagnostics; 41 | } 42 | } 43 | 44 | my MyTestApp:D $app .= new; 45 | $app.run; 46 | 47 | done-testing; 48 | -------------------------------------------------------------------------------- /t/070-flows.t: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | use Test::Async; 3 | use Vikna::Object; 4 | use Vikna::Utils; 5 | 6 | plan 3, :parallel; 7 | 8 | subtest "In context" => -> \suite { 9 | plan 6; 10 | my $obj = Vikna::Object.new; 11 | my $*VIKNA-VAR = "is here"; 12 | 13 | await $obj.flow: { 14 | suite.isa-ok: $*VIKNA-FLOW<$*VIKNA-VAR>, Failure, "plain flow doesn't see outer dynamics"; 15 | suite.isa-ok: $*VIKNA-FLOW<$*VIKNA-VAR>.exception, X::Dynamic::NotFound, "returned failure contains X::Dynamic::NotFound"; 16 | }; 17 | 18 | await $obj.flow: :name('Test Flow'), :in-context, { 19 | my $*VIKNA-VAR2 = "in flow"; 20 | suite.is: $*VIKNA-FLOW<$*VIKNA-VAR>, "is here", "in context flow sees dynamics"; 21 | $obj.flow: { 22 | suite.fails-like: 23 | { $*VIKNA-FLOW<$*VIKNA-VAR> }, 24 | X::Dynamic::NotFound, 25 | "nested plain flow doesn't see outer dynamics", 26 | :name<$*VIKNA-VAR>; 27 | } 28 | 29 | my $t = Thread.start: flow-branch { 30 | suite.is: $*VIKNA-FLOW<$*VIKNA-VAR>, "is here", "flow-branch preserves access to the dynamic context"; 31 | suite.is: $*VIKNA-FLOW<$*VIKNA-VAR2>, "in flow", "flow-branch can see originating flow context"; 32 | } 33 | $t.finish; 34 | } 35 | } 36 | 37 | subtest "Branching" => -> \suite { 38 | plan 5; 39 | my $obj = Vikna::Object.new; 40 | 41 | await $obj.flow: :name("main"), { 42 | my $vf = $*VIKNA-FLOW; 43 | my $t = Thread.start: { 44 | suite.fails-like: 45 | { $*VIKNA-FLOW }, 46 | X::Dynamic::NotFound, 47 | q‘control: $*VIKNA-FLOW is not normally accessible in an async block’, 48 | :name<$*VIKNA-FLOW>; 49 | }; 50 | $t.finish; 51 | $t = Thread.start: flow-branch { 52 | suite.ok: ?$*VIKNA-FLOW, q‘flow-branch preserves $*VIKNA-FLOW’; 53 | suite.cmp-ok: $*VIKNA-FLOW.name, 'eq', $vf.name, "flow-branch preserves flow name"; 54 | }; 55 | $t.finish; 56 | 57 | my @p; 58 | @p.push: $obj.flow: :name('non-branch'), { 59 | suite.ok: $*VIKNA-FLOW !=== $vf, q‘non-branching flow has own $*VIKNA-FLOW’; 60 | } 61 | @p.push: $obj.flow: :name('branch'), :branch, { 62 | suite.cmp-ok: $*VIKNA-FLOW.name, 'eq', $vf.name, q‘flow branch preserves the name’; 63 | } 64 | await @p; 65 | } 66 | } 67 | 68 | subtest "Return Value" => { 69 | plan 2; 70 | my $obj = Vikna::Object.new; 71 | my $rc = await $obj.flow: :name('async'), { 42 }; 72 | is $rc, 42, "async flow return value"; 73 | 74 | $rc = await $obj.flow: :name('sync'), :sync, { π }; 75 | is $rc, π, "sync flow return value"; 76 | } -------------------------------------------------------------------------------- /t/desktop/010-screen-resize.t: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | 3 | use Test::Async ; 4 | use Vikna::Events; 5 | use Vikna::Test::App; 6 | use Vikna::Utils; 7 | use Vikna::Rect; 8 | 9 | plan 3; 10 | 11 | class MyDesktop 12 | is Vikna::Desktop 13 | { 14 | my @bg-chars = <+ * .>; 15 | multi method event(Event::Screen::Geom:D $ev) { 16 | my $chr = @bg-chars.shift; 17 | self.cmd-setbgpattern: $chr; 18 | @bg-chars.push: $chr; 19 | nextsame; 20 | } 21 | } 22 | 23 | class MyApp is Vikna::Test::App { 24 | method build-desktop { 25 | self.test-init: self.create: MyDesktop, |%.desktop-profile, :geom($.screen.geom.clone); 26 | } 27 | 28 | method test-init($desktop) { 29 | my $test-suite = test-suite; 30 | 31 | my sub init-event($ev, $task) { 32 | my $screen = $.screen; 33 | await subtest "initial state" => { 34 | is $ev.id, 0, "the first init event id is 0"; 35 | is-rect-filled $screen.buffer, $screen.geom, "screen is empty", 36 | :char(""), :fg(""), :bg(""), :style(VSTransparent); 37 | } 38 | } 39 | 40 | my sub post-redraw($ev, $task) { 41 | my $screen = $.screen; 42 | my $desktop = $.desktop; 43 | await subtest "filled screen" => { 44 | is-rect-filled $screen.buffer, $screen.geom, "screen is filled by desktop", 45 | :char($desktop.bg-pattern), :fg($desktop.fg), :bg($desktop.bg), :style($desktop.style); 46 | } 47 | } 48 | 49 | my sub start-resize($passed, $task) { 50 | # Only try resizing if the initial sequnce passed 51 | if $passed { 52 | self.test-resize: $test-suite; 53 | } 54 | else { 55 | $.desktop.quit; 56 | } 57 | } 58 | 59 | is-event-sequence $desktop, [ 60 | %( 61 | type => Event::Init, 62 | checker => &init-event, 63 | # accept => -> $ev { diag "accepting $ev"; True }, 64 | # checker => -> $ev { diag "checker $ev"; True }, 65 | # orig => $desktop, 66 | # disp => $desktop, 67 | message => "desktop init", 68 | ), 69 | %( 70 | type => Event::Cmd::Redraw, 71 | message => "first redraw", 72 | ), 73 | %( 74 | type => Event::Flattened, 75 | message => "first canvas flattening", 76 | ), 77 | %( 78 | type => Vikna::Event::Updated, 79 | message => "submitted to the screen", 80 | ), 81 | %( 82 | type => Event::Screen::Ready, 83 | checker => &post-redraw, 84 | message => "screen updated with desktop", 85 | on-status => &start-resize, 86 | ) 87 | ], "Desktop init sequence", :async, :timeout(30), 88 | # :trace-events, 89 | :defaults{ on-status => -> $passed, $ { $.desktop.quit unless $passed } }; 90 | 91 | $desktop 92 | } 93 | 94 | method test-resize($test-suite) { 95 | my $dest-rect = Vikna::Rect(80, 25); 96 | 97 | my sub check-geoms($ev, $) { 98 | await subtest "geoms changed" => { 99 | is $ev.to, $dest-rect, "event 'to' geometry is correct"; 100 | is $.screen.geom, $dest-rect, "screen geometry changed"; 101 | is $.desktop.geom, $dest-rect, "desktop geometry matches that of the screen"; 102 | } 103 | } 104 | 105 | my sub desktop-geom-change($ev, $) { 106 | $ev.geom == $dest-rect 107 | } 108 | 109 | my sub post-sgc-redraw($ev, $) { 110 | is-rect-filled $.screen.buffer, $dest-rect, "screen redrawn", 111 | :attr($.desktop.attr), 112 | :char<+> # Pattern char is rotated on each resize by MyDesktop event method. This is the first resize. 113 | } 114 | 115 | $test-suite.is-event-sequence: $.desktop, [ 116 | %( 117 | type => Event::Screen::Geom, 118 | message => "screen geom notification", 119 | checker => &check-geoms, 120 | ), 121 | %( 122 | type => Event::Changed::Geom, 123 | message => 'desktop geometry changed', 124 | checker => &desktop-geom-change, 125 | ), 126 | %( 127 | type => Event::Updated, 128 | message => "desktop redrawn", 129 | checker => &post-sgc-redraw, # post-screen-geom-change-redraw 130 | # We're done, quit 131 | on-status => -> | { $.desktop.quit }, 132 | ), 133 | ], "Screen resized", :async, :timeout(30), 134 | # :trace-events, 135 | :defaults{ 136 | on-status => -> $passed, $ { $.desktop.quit unless $passed } 137 | }; 138 | 139 | $.screen.test-geom = $dest-rect; 140 | } 141 | 142 | method main(|) { 143 | self.self-diagnostics; 144 | # $.desktop.quit; 145 | } 146 | } 147 | 148 | # MyApp.new.run; 149 | given MyApp.new(:!debugging) { 150 | .run 151 | } 152 | 153 | done-testing; -------------------------------------------------------------------------------- /t/widget/010-basic.t: -------------------------------------------------------------------------------- 1 | use v6.e.PREVIEW; 2 | use Test::Async ; 3 | use Vikna::Events; 4 | use Vikna::Test::App; 5 | use Vikna::Utils; 6 | use Vikna::Widget; 7 | use Vikna::Rect; 8 | 9 | class MyWidget is Vikna::Widget { 10 | submethod TWEAK(|) { 11 | self.test-startup; 12 | self.test-move; 13 | } 14 | 15 | method !is-drawn($ev, $task) { 16 | my $screen = $.app.screen.buffer; 17 | my $desktop = $.app.desktop; 18 | my $dgeom = $desktop.geom; 19 | my $dattr = $desktop.attr; 20 | is-rect-filled $screen, $.geom, "widget is on the desktop", :attr($.attr); 21 | is-rect-filled $screen, Vikna::Rect(0, 0, $dgeom.w, $.y), "desktop above the widget", :attr($dattr); 22 | is-rect-filled $screen, Vikna::Rect(0, $.geom.bottom + 1, $dgeom.w, $dgeom.h - $.geom.bottom), 23 | "desktop below the widget", :attr($dattr); 24 | is-rect-filled $screen, Vikna::Rect(0, $.y, $.x, $.h), "desktop left to the widget", :attr($dattr); 25 | is-rect-filled $screen, Vikna::Rect($.geom.right + 1, $.y, $dgeom.w - $.geom.right, $.h), 26 | "desktop right to the widget", :attr($dattr); 27 | } 28 | 29 | method test-startup { 30 | is-event-sequence $.app.desktop, 31 | [ 32 | evs-task( Event, :skip-until('widget ready'), ), 33 | evs-task( 34 | Event::Screen::Ready, 35 | "desktop drawn on the screen", 36 | :skip-until('widget redrawn'), 37 | :checker( -> |c { self!is-drawn(|c) } ), 38 | :on-status( -> $passed, $ { 39 | evs-syncer('move widget').signal($passed); 40 | self.set-geom(42,12,15,10); 41 | } ), 42 | ), 43 | ], 44 | "desktop with widget to screen", 45 | :async, 46 | :timeout(30), 47 | # :trace-events, 48 | :defaults{ :quit-on-flunk }; 49 | 50 | is-event-sequence self, 51 | [ 52 | evs-task( Event::Init, "initialized", 53 | :on-status( -> $passed, $ { evs-syncer('widget ready').signal($passed); } )), 54 | evs-task( Vikna::Event::Flattened, "widget redrawn and canvas flattened", 55 | on-status => -> $passed, $, { 56 | evs-syncer('widget redrawn').signal($passed); 57 | }, 58 | ), 59 | evs-task( Event::Updated, "widget imprinted on desktop" ) 60 | ], 61 | "Widget startup", 62 | :async, 63 | :timeout(20), 64 | # :trace-events, 65 | :defaults{ :quit-on-flunk }; 66 | } 67 | 68 | method test-move { 69 | is-event-sequence $.app.desktop, 70 | [ 71 | # evs-task(Event, "awaiting for widget change", :skip-until('widget post-geom')), 72 | evs-task(Event::Screen::Ready, "desktop content after widget change", 73 | :skip-until('widget post-geom'), 74 | :checker( -> |c { self!is-drawn(|c) } ), 75 | :on-status( -> $passed, $ { $.app.desktop.quit } )), 76 | ], 77 | "desktop on widget move", 78 | :async, 79 | # :trace-events, 80 | :defaults{ :quit-on-flunk }; 81 | 82 | is-event-sequence self, 83 | [ 84 | evs-task( Event::Changed::Geom, "wait for geom", 85 | :skip-until('widget set geom command'), 86 | :checker( -> $ev, $ { $ev.to == Vikna::Rect(42,12,15,10) } ), 87 | ), 88 | ], 89 | "Change geometry notification", 90 | :async, 91 | :defaults{ :quit-on-flunk }; 92 | 93 | is-event-sequence self, 94 | [ 95 | evs-task( Event::Cmd::SetGeom, "set geometry command event", 96 | :skip-until('move widget'), 97 | :on-status( -> $passed, $ { 98 | $.app.desktop.flatten-block; 99 | evs-syncer('widget set geom command').signal($passed); 100 | } )), 101 | # evs-task( Event::Cmd::Redraw, "post-geom change redraw" ), 102 | evs-task( Event::Flattened, "widget submitted to parent", 103 | :on-status( -> $passed, $ { 104 | evs-syncer('widget post-geom').signal($passed); 105 | $.app.desktop.flatten-unblock; 106 | } )), 107 | evs-task( Event::Updated, "widget submitted to parent" ), 108 | ], 109 | "Change geometry", 110 | # :trace-events, 111 | :async, 112 | :defaults{ :quit-on-flunk }; 113 | } 114 | } 115 | 116 | class MyApp is Vikna::Test::App { 117 | method main(|) { 118 | $.desktop.create-child: MyWidget, :x(10), :y(5), :w(20), :h(5), :attr{ 119 | :pattern<❄︎>, :fg, :bg, :style(VSNone), 120 | }; 121 | } 122 | } 123 | 124 | plan 5; 125 | given MyApp.new(:!debugging) { 126 | .run 127 | } 128 | 129 | done-testing; 130 | -------------------------------------------------------------------------------- /tools/tracer: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env raku 2 | use v6.e.PREVIEW; 3 | use Vikna::Tracer; 4 | use Template::Mustache; 5 | # use Data::Dump; 6 | 7 | my %*SUB-MAIN-OPTS = 8 | :named-anywhere, 9 | ; 10 | 11 | multi MAIN('list-sessions', Str :$db-file = 'Vikna.sqlite') { 12 | my $tr = Vikna::Tracer.new: db-name => $db-file; 13 | for $tr.sessions { 14 | say .id.fmt('%4d '), ~.started.local, " [{.records.elems.fmt('%6d')}] ", .name; 15 | } 16 | } 17 | 18 | multi MAIN( Str:D $format where * ~~ (any Vikna::Tracer.templates.keys), 19 | Int :$session?, 20 | Str:D :$db-file = 'Vikna.sqlite', 21 | Str :o(:$output) 22 | ) 23 | { 24 | my $tr = Vikna::Tracer.new: db-name => $db-file; 25 | my $fh = $*OUT; 26 | LEAVE $fh.close if $output; 27 | with $output { 28 | $fh = .IO.open: :w; 29 | } 30 | 31 | my $tmpl = Vikna::Tracer.templates{$format}; 32 | 33 | my @sessions; 34 | with $session { 35 | @sessions = $tr.session($_) 36 | } 37 | else { 38 | @sessions = $tr.sessions; 39 | } 40 | 41 | say "Loaded ", +@sessions, " sessions"; 42 | 43 | my %data = session => []; 44 | for @sessions -> $sess { 45 | say "Processing session ", $sess.id, ", records: ", $sess.records.elems; 46 | my %sess = id => .id, started => .started, name => .name with $sess; 47 | my @flows = $sess.records.flows; # $sess.records.map( *.flow-id ).unique.sort; 48 | my @flow-names = '' xx +@flows; 49 | my %fl-idx = @flows.antipairs; 50 | %sess = @flows.map( { { flow => ~$_ } } ).list; 51 | %sess = @flows.elems + 2; 52 | my $reccount = 0; 53 | %sess = (gather { 54 | for $sess.records { 55 | ++$reccount; 56 | $*ERR.print: $reccount.fmt("REC: %6d\r") if $reccount % 100 == 0; 57 | my %rec; 58 | my $fl-idx = %fl-idx{ .flow-id }; 59 | 60 | if @flow-names[$fl-idx] ne .flow-name { 61 | my %flow-row; 62 | my @flow-cols = [ { cell => { } } xx +@flow-names ]; 63 | @flow-cols[$fl-idx] = %( flow_name => (.flow-name || '*anon*') ); 64 | @flow-names[$fl-idx] = .flow-name; 65 | %flow-row = .id; 66 | %flow-row