Skip to content

Commit

Permalink
v2.0 roadmap (#174)
Browse files Browse the repository at this point in the history
* init

* Pulling Abstract Trees into 2.0 (#198)

* CompatHelper: add new compat entry for WordTokenizers at version 0.5, (keep existing compat) (#171)

Co-authored-by: CompatHelper Julia <[email protected]>

* Stack traces (#179)

* styling

* now excludes other installed packages + fmt and tests

* small improvements ot hidden frames

* format and docs

* small fixes

* fmt

* kwarg func call fmt

* show each error line code

* covergage

* stacktrace formatting

* traces rendering improvements

* test fix

* new text reshaping function from chatgpt

* fixed tests

* cleanup

* cleanup

* bump

* bump

* bump

* prompt init

* small fixes to repr and error

* fixed small repr bugs

* removed deps

* wip errors

* method error kwargs call

* parsed kwargs calls in stackrace

* fixes

* method error edge cases

* bump

* bump

* bump

* fixed textlen bug for orphaned tags

* Update Project.toml

* Update Project.toml

* errors with no backtrace are now shown

* logger doesn't cut msgs

* CI fix

* error msg panel now always shows

* reworked logs rendering code

* msg reshaping

* refactored stacktrace code + further logs improvements

* fixed bug with markdown rendering

* Link renderable development (#180)

* bump

* bump

* working on making Link work

* functional link?

* fixing them errors

* fixed behavior of hidden frames in stacktrace and docs

* fixed bugs

* bugs fixed and more test

* bugfix

* tests fix

* tests fixing

* tests fix

* tests fix

* Fix #181 (#182)

* small fixes

* bump

* `Prompt` (#175)

* prompt init

* small fixes to repr and error

* fixed small repr bugs

* removed deps

* wip errors

* bump

* bump

* bump

* bump

* prompt wip

* bump

* prompt almost finished

* added tests and docs for prompt

* bump

* coverage and CI

* docs fix

* Update _error_messages.jl

* Annotations (#185)

* init

* more extensive precompilation

* small fixes for error IO

* 204 method invalidation at startup ops

* bump

* working on annotation

* added style

* bump

* disabled precompilation

* new code reshaping function

* new annotation system

* tests for annotations passing

* fmt

* more tests

* bump

* bump

* ci fix

* bump

Co-authored-by: Federico Claudi <[email protected]>

* Create Invalidations.yml

* Create unused_dependencies.yml

* Update Invalidations.yml

* Update Invalidations.yml

* Update CI.yml

* Update CI.yml

* Coverage - adding more tests (#186)

* increasing test coverage

* fixed bug with markup tags including newline

* test coverage

* bump

* bump

* bump

* bump

* bump

* CI fix

* CI fix

* CI fix

* CI fix

* bump

* Update CI.yml

* bump

* refactored tree

* CI fix

* docs

* Update unused_dependencies.yml

* ci fix

* Update Invalidations.yml

* removed fillin(segments)

* Update CI.yml

* fmt

* bump

* removed error message functionality, using base

* bump

* fix_markup_across_lines (#188)

* fix_markup_across_lines

* tests passing

* fixed small things

* bump

* wip for fix_ansi_across_lines

* fixed nested style tags

* reshaping with ANSI and markup works well

* fmt

* bump

* bump

* bump

* removed unused funcs

* docstring

* small fixes

* bump

* Update CI.yml

* Working on pre-compilation (#173)

* init

* more extensive precompilation

* small fixes for error IO

* 204 method invalidation at startup ops

* bump

* cleaned up precompilation

* removed deps

* bump

Co-authored-by: Federico Claudi <[email protected]>

* CompatHelper: add new compat entry for AbstractTrees at version 0.4, (keep existing compat) (#189)

Co-authored-by: CompatHelper Julia <[email protected]>

* bump

* no stdout in precomp (#191)

* no stdout in precomp

* ignore precompilation code in coverage

* finished re-factoring tree

* CI fix

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: CompatHelper Julia <[email protected]>
Co-authored-by: Rémi Vezy <[email protected]>
Co-authored-by: Federico Claudi <[email protected]>

* Live (#199)

* Live first push

* bump

* bump

* pager almost ready

* pager ready

* making progress with menu

* making progress with menu

* hacky but working

* reduced flicker

* progress on making progress a Live

* redirect tsdout

* bump

* test

* introspection fmt + error fmt

* bump

* bump

* CompatHelper: add new compat entry for WordTokenizers at version 0.5, (keep existing compat) (#171)

Co-authored-by: CompatHelper Julia <[email protected]>

* Stack traces (#179)

* styling

* now excludes other installed packages + fmt and tests

* small improvements ot hidden frames

* format and docs

* small fixes

* fmt

* kwarg func call fmt

* show each error line code

* covergage

* stacktrace formatting

* traces rendering improvements

* test fix

* new text reshaping function from chatgpt

* fixed tests

* cleanup

* cleanup

* bump

* bump

* bump

* prompt init

* small fixes to repr and error

* fixed small repr bugs

* removed deps

* wip errors

* method error kwargs call

* parsed kwargs calls in stackrace

* fixes

* method error edge cases

* bump

* bump

* bump

* fixed textlen bug for orphaned tags

* Update Project.toml

* Update Project.toml

* errors with no backtrace are now shown

* logger doesn't cut msgs

* CI fix

* error msg panel now always shows

* reworked logs rendering code

* msg reshaping

* refactored stacktrace code + further logs improvements

* fixed bug with markdown rendering

* Link renderable development (#180)

* bump

* bump

* working on making Link work

* functional link?

* fixing them errors

* fixed behavior of hidden frames in stacktrace and docs

* fixed bugs

* bugs fixed and more test

* bugfix

* tests fix

* tests fixing

* tests fix

* tests fix

* Fix #181 (#182)

* small fixes

* bump

* `Prompt` (#175)

* prompt init

* small fixes to repr and error

* fixed small repr bugs

* removed deps

* wip errors

* bump

* bump

* bump

* bump

* prompt wip

* bump

* prompt almost finished

* added tests and docs for prompt

* bump

* coverage and CI

* docs fix

* Update _error_messages.jl

* Annotations (#185)

* init

* more extensive precompilation

* small fixes for error IO

* 204 method invalidation at startup ops

* bump

* working on annotation

* added style

* bump

* disabled precompilation

* new code reshaping function

* new annotation system

* tests for annotations passing

* fmt

* more tests

* bump

* bump

* ci fix

* bump

Co-authored-by: Federico Claudi <[email protected]>

* Create Invalidations.yml

* Create unused_dependencies.yml

* Update Invalidations.yml

* Update Invalidations.yml

* Update CI.yml

* Update CI.yml

* Coverage - adding more tests (#186)

* increasing test coverage

* fixed bug with markup tags including newline

* test coverage

* bump

* bump

* bump

* bump

* bump

* CI fix

* CI fix

* CI fix

* CI fix

* bump

* Update CI.yml

* bump

* refactored tree

* CI fix

* docs

* Update unused_dependencies.yml

* ci fix

* Update Invalidations.yml

* removed fillin(segments)

* Update CI.yml

* fmt

* bump

* removed error message functionality, using base

* bump

* fix_markup_across_lines (#188)

* fix_markup_across_lines

* tests passing

* fixed small things

* bump

* wip for fix_ansi_across_lines

* fixed nested style tags

* reshaping with ANSI and markup works well

* fmt

* bump

* bump

* bump

* removed unused funcs

* docstring

* small fixes

* bump

* Update CI.yml

* Working on pre-compilation (#173)

* init

* more extensive precompilation

* small fixes for error IO

* 204 method invalidation at startup ops

* bump

* cleaned up precompilation

* removed deps

* bump

Co-authored-by: Federico Claudi <[email protected]>

* CompatHelper: add new compat entry for AbstractTrees at version 0.4, (keep existing compat) (#189)

Co-authored-by: CompatHelper Julia <[email protected]>

* bump

* restored to old LiveInternals

* small improvements

* simple menu

* working on help message

* working on help

* added multi select menu

* bump

* help tooltip working

* small fixes

* pager with liner numbers and scrollbar

* tab viewer refactor

* improved key controls

* refactor

* more widgets and improvements

* tabviewer improvemens

* fixed scrollbar

* fixed introspection

* compositor accepts fractional values

* added apply_style to ren and removed extra style resets from boxes and panels

* removed apply style for renderables and working on app

* menu can be horizontal

* added gallery

* refactor

* small fixes

* RGB example

* inspect re-worked

* refactor

* bump

* reverted to master progress bar

* tests passing

* tests passing

* refactoring controls

* no stdout in precomp (#191)

* no stdout in precomp

* ignore precompilation code in coverage

* working on controls

* debug mode and working controls

* small imrpvoements

* removed live internals from widgets

* all widgets play through app

* bump

* small fixes

* working on responsive app size

* working on resposive

* pager improvements

* tictactoe

* small improvements

* tests fix

* bump

* widget internals

* transitions inferred from layout

* wip

* all widgets work

* added help msg

* help msg

* bump

* finished re-factoring tree

* CI fix

* added test

* CI fix

* bump

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: CompatHelper Julia <[email protected]>
Co-authored-by: Rémi Vezy <[email protected]>
Co-authored-by: Federico Claudi <[email protected]>

* working on docs

* bump

* Fixing known v2.0 problems ahead of release (#200)

* fixed logs problems

* bump

* fixed layout in repr functionality

* fixed err msg not showing up in progress

* added option to expand stackatrace width

* CI fix

* CI fix

* CI fix

* CI fix

* CI fix

* CI fix

* CI fix

* working on docs

* bump

* docs

* docs fix

* merged

---------

Co-authored-by: Federico Claudi <[email protected]>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
Co-authored-by: CompatHelper Julia <[email protected]>
Co-authored-by: Rémi Vezy <[email protected]>
  • Loading branch information
5 people authored Feb 6, 2023
1 parent 5af6831 commit c142c5c
Show file tree
Hide file tree
Showing 448 changed files with 7,285 additions and 2,349 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
scratch.txt
*.jl.*.cov
*.jl.cov
*.jl.mem
Expand Down
2 changes: 2 additions & 0 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ MyterialColors = "1c23619d-4212-4747-83aa-717207fae70f"
OrderedCollections = "bac558e1-5e72-5ebc-8fee-abe8a469f55d"
Parameters = "d96e819e-fc66-5662-9728-84c9c7592b0a"
ProgressLogging = "33c8b6b6-d38a-422a-b730-caa89a2f386c"
REPL = "3fa0cd96-eef1-5676-8a61-b3b8758bbffb"
SnoopPrecompile = "66db9d55-30c0-4569-8b51-7e840670fc0c"
Tables = "bd369af6-aec1-5ad0-b16a-f7cc5008161c"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"
Expand All @@ -40,6 +41,7 @@ StableRNGs = "860ef19b-820b-49d6-a774-d7a799459cd3"
Suppressor = "fd094767-a336-5f1f-9728-57cf17d0bbfb"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
TimerOutputs = "a759f4b9-e2f1-59dc-863e-4aeb61b1ea8f"
UUIDs = "cf7118a7-6976-5b1a-9a39-7adc72f591a4"

[targets]
test = ["Random", "StableRNGs", "Suppressor", "Test", "TimerOutputs"]
4 changes: 2 additions & 2 deletions README.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
using Term
import Term.Renderables: Renderable
import Term: rint
import Term: typestree, inspect
import Term: inspect
using Term.Layout
import Term.Measures: height

Expand Down Expand Up @@ -172,7 +172,7 @@ lorem1 = TextBox(_lorem; width = 62, fit = false, padding = (0, 0, 0, 0))
lorem2 = TextBox(_lorem; width = 42, fit = false, padding = (0, 0, 0, 0))

expr = :(2x + 2π / θ)
tree = Renderable(sprint(typestree, Float64))
tree = Renderable(sprint(Tree, Float64))
dendo = Renderable(sprint(inspect, expr))
renderables_info = TextBox(
"""{bold bright_blue}Renderables types{/bold bright_blue}
Expand Down
7 changes: 6 additions & 1 deletion docs/src/adv/errors_tracebacks.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,9 @@ import Term: STACKTRACE_HIDDEN_MODULES, STACKTRACE_HIDE_FRAMES
STACKTRACE_HIDDEN_MODULES[] = ["REPL", "OhMyREPL"] # list names of modules you want ignored in the stacktrace
STACKTRACE_HIDE_FRAMES[] = false # set to true to hide frame, false to show all of them
```
<<<<<<< HEAD
```

=======
```
>>>>>>> 5af6831f4379fc6bbb39622d06c91a17dcbac183
13 changes: 13 additions & 0 deletions docs/src/api/api_live.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Live
```@meta
CurrentModule = Term.LiveWidgets
```


```@index
Pages = ["api_live.md"]
```

```@autodocs
Modules = [LiveWidgets]
```
9 changes: 8 additions & 1 deletion docs/src/layout/compositor.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
# Compositor
# [Compositor](@id CompositorDocs)
We've just seen how you can use `grid` to achieve nice layouts very easily. That's great when you have a bunch of renderables of the same size and you want to crate a simple layout. If you want to get fancy using grid is not trivial:

```@example compositor
Expand Down Expand Up @@ -90,6 +90,13 @@ Compositor(layout)
easy peasy.


Some of you might argue: but this way I need to know exactly what size each element has to have in order to fit in the available terminal real estate... that's annoying! Nope, just use `Float64` as dimensions values to specify that you want to fill that fraction of the available space:
```@example compositor
layout = :(A(20, $(0.75)) * B(20, $(0.25)))
Compositor(layout)
```


## Compositor content
Some of you will be thinking: "this is all well and good, but I don't want to just show place holders I've got actual content!". Fair enough, so let's add some.

Expand Down
122 changes: 122 additions & 0 deletions docs/src/live/app_intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
# App

The starting point of any good "live" or interactive terminal display is an `App`. The `App` takes care of generating and updating the visuals as well as taking in user input making use of it (e.g. to update the display accordingly).
An app has some content. This content is in the form of `AbstractWidget` elements. These widgets are single content elements that serve a specific function, for example displaying some text or acting as buttons etc. More on widgets later. In addition to knowing **what** is in an app, we also need to specify **how** it should look like. Specifically, how should the different widgets be layed out. So in addition to creating widgets, you will need to specify a layout for your app.
There's a lot more to apps, but for now we can start with some simple examples

### A first example
We start with a simple example: an app that only shows one single widget. a `TextWidget` displaying a bit of text.

```@example app
using Term
using Term.LiveWidgets
app = App(
TextWidget("""
The starting point of any good "live" or interactive terminal display is an `App`. The `App` takes care of generating and updating the visuals as well as taking in user input making use of it (e.g. to update the display accordingly).
An app has some content. This content is in the form of `AbstractWidget` elements. These widgets are single content elements that serve a specific function, for example displaying some text or acting as buttons etc. More on widgets later. In addition to knowing **what** is in an app, we also need to specify **how** it should look like. Specifically, how should the different widgets be layed out. So in addition to creating widgets, you will need to specify a layout for your app.
There's a lot more to apps, but for now we can start with some simple examples
""")
)
```

Easy. Now to *use* the app you'd call `play(app)`. This starts an interactive session in which the app continously refreshes its display and reacts to user input until the app is exited by pressing `q` or `Esc`.
Unfortunately we can't do that here in the docs, but we can use `frame` to see what the app would look like
when we start it:

```@example app
frame(app)
```

!!! tip "Ask for help"
Press `h` while using `play` to interact with an app to display a help tooltip.

### Layout
Ok, one widget apps are not that useful.
When adding more widgets you'll eventually have to specify how to lay them out. Essentially you want to specify the space taken by the app as a whole (the width and height in the terminal) and then within that you need to specify the size of each
widgets and where they are located (e.g. widget A is to the left of B and A,B together are above C).

To specify the layout you need to use an `Expr` like the one used for ['Compositor'](@ref CompositorDocs) content.
The expression is made of elements like `a(h, w)` where `a` is the layout element's name and `h,w` are the height and width of the element.
Note that `h` has to be an integer (the number of lines spanned by the widget) but `w` can be either `Int` (number of columns) or `Float64` with `0 < w < 1` to specify the fraction of the available space that should be used.

For example:
```julia
:r(10, 0.5)
```
says that the widget `r` should take up 10 lines and half of the available width.

In addition to elements, you can use `*` and `/` to specify the relation between elements: `*` means "to the side of" and `/` means "above".
Combined with parenteses you can get some pretty complex layouts. For example:

```@example app
layout = :(
:(( a(10, .5) * b(10, .5) )/c(10, 1))
)
```

With a layout in mind you can start creating your app.
Eventually you'll need to provide some widgets too, but if you just want to check the layout (and maybe tweak it) you can
create an empty app with placeholders to visualize the position of each widget:

```@example app
App(layout) |> frame
```

Note that you can always specify the `width` and `height` of the app. If you don't, the app will try to use the full terminal size.


!!! tip "Responsive layout"
If you use a `Float` to specify your layout elements size, the app will automatically resize the elements when the terminal
size is reduced. If you also want your app to expand to fill in the whole terminal is the terminal is enlarged, you can use
`expand` keyworad argument for `App`.



### Adding widgets
To create an app with multiple widgets, you'll need the layout info as shown above and a `Dict` with
the widgets you want your app to display. The keys in the `Dict` need to match the layout elemnts names.
For example, to create an app showing two pieces of text.

```@example app
layout = :(a(25, .5) * b(25, .5))
widgets = Dict(
:a => TextWidget("""To create an app with multiple widgets, you'll need the layout info as shown above and a `Dict` with
the widgets you want your app to display. The keys in the `Dict` need to match the layout elemnts names.
For example, to create an app showing two pieces of text.
"""; as_panel=true),
:b => TextWidget("""
The starting point of any good "live" or interactive terminal display is an `App`. The `App` takes care of generating and updating the visuals as well as taking in user input making use of it (e.g. to update the display accordingly).
An app has some content. This content is in the form of `AbstractWidget` elements. These widgets are single content elements that serve a specific function, for example displaying some text or acting as buttons etc. More on widgets later. In addition to knowing **what** is in an app, we also need to specify **how** it should look like. Specifically, how should the different widgets be layed out. So in addition to creating widgets, you will need to specify a layout for your app.
There's a lot more to apps, but for now we can start with some simple examples
"""; as_panel=true)
)
App(layout; widgets=widgets) |> frame
```

Note that one panel is dim while the other is not, why?
That's because the app considers the first widget to be 'active' and the brighter color signals that.
Different widgets show that they are active differently, but generally they use colors to signal to the user that they are
the currently active one. This is important because user input's (like key presses) will be passed to the currently active widget.
For example, if you have a widget that is a button and it get's pressed by using `spacebar`, then pressing space
will only work if the button is active.

To change the currently active widget you can "navigate" through the app using arrow keys.
`App` analyzes the `layout` of the app to infer the relative position of the wedgets and set up the navigation accordingly.
To test this, use `play` on the app we just created and then left/right arrow to change focus!
(Don't forget to use `q` to exit the app when you're done)

Sometimes you want different ways to specify which widget should be active, either by using an arrow key or by pressing a specific key.
You can pass `transition_rules` to `App` to use your own set of rules. `transition_rules` should be a `Dict` with `KeyInput` types as key and a `Dict` of pairs `Symbol => Symbol`. The interpretation is that the symbols indicate the current and next widget to be active when the key is pressed. For example:

```julia
transition_rules = Dict(
ArrowRight() => Dict(:a => :b),
ArrowLeft() => Dict(:b => :a),
)
```
implements the transition rules from the example above.
5 changes: 5 additions & 0 deletions docs/src/live/intro.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Live widgets

Allright, making fancy terminal graphics is all good and well, but a bit boring isn't it? Wouldn't it be nice to have something more dynamics. Something that updates over times and maybe even reacts to users input? Well, that's what live widgets are for. Live widgets are a way to make your terminal application more interactive. They are a way to make your terminal application more dynamic. They are a way to make your terminal application more fun.

Let's go.
38 changes: 38 additions & 0 deletions docs/src/live/keyboard_input.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Keyboard input
Apps can capture input from the user by reading key presses and passing them to the active widget.

Each widget must have a `controls` attribute with a dictionary mapping keys to function.
The keys of this dictionary can take one of two types:
- `Char`: a single character. Used to map a "letter" key to a function. For example `q` generally quits the app and `h` displays help.
- a `KeyInput` type like `ArrowLeft()` or `HomeKey()`. These are special keys including the arrows, page up/down, Esc, Del and SpaceBar.


The values of the dictionary are functions. The function should have a signature: `fn(w, k)` where `w` is the widget they are assigned to and `k` is the key that was pressed.
For example in `Pager` you can use `ArrowRight, PageDownKey` and `]` to scroll down so a function is defined as:
```julia
next_page(p::Pager, ::Union{PageDownKey,ArrowRight,Char})
```

and with similar functions the `controls` for `Pager` are:
```
pager_controls = Dict(
ArrowRight() => next_page,
']' => next_page,
ArrowLeft() => prev_page,
'[' => prev_page,
ArrowDown() => next_line,
'.' => next_line,
ArrowUp() => prev_line,
',' => prev_line,
HomeKey() => home,
EndKey() => toend,
Esc() => quit,
'q' => quit,
)
```

there's no restriction to which/how many control functions you should have and how they should affect your widget.
However having `quit` is generally a good idea or the user can't quit the app while that widget is active!
Different widgets will have different controls.
A widget's controls are only activated if the corresponding key is pressed while the widget is active.
118 changes: 118 additions & 0 deletions docs/src/live/widgets.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
# Widgets

Widgets are the building blocks that `App`s are made of.
A widget is generally a single piece of content with a specific function (display text, act as a button...).

## Predefined widgets
`Term` comes with a bunch of widgets you can already plug into your applications.
If you want to develop your own, read below.

### TextWidget
We've seen this already, it's the simplest widget: just show some text.


```@example widgets
using Term.LiveWidgets
TextWidget("Hello world!") |> frame
```

unlike other widgets it doesn't have a whole lot of options, just gives you the
choice of showing just text or a panel:
```@example widgets
TextWidget("Hello world!", as_panel=true) |> frame
```

### InputBox
This widget is a bit more complex. It's a text input box. It's a bit like a `TextWidget` but it also allows the user to type in it.
When this widget is active, any key press get's captured and displayed as text in the widget.
As usual things like Space Bar, Enter and Del add spaces, new lines and delete characters respectively.
Make an `App` with an `InputBox` to see how it works!

### Buttons
```@example widgets
Button("Click me please!") |> frame
```
You can specify the color of the text and the button. You can also pass a `callback`: a `Function` that gets called
when the button is pressed.

Normally, when pressed, a button will change its color to indicate that it has been pressed and then revert to its
original style. If you want something that acts like a toggle switch, use `ToggleButton` instead.



### Menus

The idea is simple, provide the user with some option and let them choose one.
```@example widgets
SimpleMenu(["Option 1", "Option 2", "Option 3"]) |> frame
```

the user can use the arrow keys to navigate the menu and press Enter to select an option.
Selecting an options quits the application and returns an integer with the index of the selected option.
`active_style, inactive_style` can be used to set the style of the currently active options while the user
navigates the menu. The `layout` options lets you choose if you want the options to be displayed horizontally or vertically.
If you want the menu elements to stand out more, you can use `ButtonsMenu`.


If you want to let users select more than one option at once, use `MultiSelectMenu`. It shows a checkbox like display and
users can use the space bar to toggle the state of the checkbox.

### Pager
A pager is a widget that lets you display a lot of text in a scrollable window.
```@example widgets
Pager("This is a pager. It lets you display a lot of text in a scrollable window."^300) |> frame
```

You can use various keyboard inputs to navigate the pager (arrows to move up and down and to page up and down, Home and End to go to the top and bottom of the text respectively).

You can specify the size (height and width) of the pager and if line numbers should be shown (useful to display code):

```@example widgets
Pager("This is a pager. It lets you display a lot of text in a scrollable window."^300, height=20, width=20, line_numbers=true) |> frame
```


### Gallery

A `Gallery` is somewhat inbetween a widget and an `App`. It's a container for other widgets.
Only one widget at the time is displayed in the space taken by the `Gallery`.

```@example widgets
g = Gallery(
[TextWidget("Hello world!"), TextWidget("Not shown")];
height = 25, width=60, title="My gallery"
) |> frame
```

Use arrows to change which widget is active.


## Defining widgets
All widgets are subtypes of the `abstract type AbstractWidget`.
A new widget type needs to be defined as a `mutable struct` and it needs to have two obligatory fields:
```julia
internals::WidgetInternals
controls::AbstractDict
```

`WidgetInternals` is a struct that contains the state of the widget. It keeps track of thigns like the size
of the widget and the three callback functions `on_draw`, `on_activated` and `on_deactivated`.
These are optional functions that are called when `frame` is called on a widget or when the widget is activated or deactivated.
The activated/deactivated functions can be used to change the appearance of the widget to signal to the user that the widget is active or not.

`controls` is a `Dict{Union{Char, KeyboardInput}, Function}` that says how keyboard inputs should be used: it maps a keyboard input to a function that gets called when that input is pressed if the widget is active.

Other than these obligatory fields, the `struct` needs to have anything that the widget needs to work.

Two obligatory methods need to be defined for the widget to work.
`frame(w::MyWidget; kwargs...)::AbstractRenderbles` is the function that gets called when the app's display is updated. The renderable that is returned is what gets displayed on the screen.
`on_layout_change(w::MyWidget, m::Measure)` says what should happen when the app gets resized. The `Measure` is the new size of the widget.
Usually it's enough to do something like:
```julia
on_layout_change(t::TextWidget, m::Measure) = t.internals.measure = m
```


To get an idea of how to define a widget, take a look at the source code of the widgets that come with `Term`.
8 changes: 3 additions & 5 deletions docs/src/ren/panel.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,17 +88,15 @@ You can justify the panel's content to `:left, :center, :right`!
```
And style the title and subtitle, or the whole background too:
```@example panel
import Term: highlight_syntax, apply_style, do_by_line, fillin
syntax_with_bg(t) = do_by_line(ln -> apply_style(ln, "on_red"), fillin(t) |> highlight_syntax)
import Term: highlight_syntax
Panel(
syntax_with_bg("""
highlight_syntax("""
function show_off(x)
print(x)
end
""");
background="on_red"
background="on_black", fit=true, style="on_black"
)
```
Expand Down
Loading

0 comments on commit c142c5c

Please sign in to comment.