Skip to content

Malef is a terminal/console-handling library written in Ada for TUI applications.

License

GPL-3.0, Unknown licenses found

Licenses found

GPL-3.0
LICENSE
Unknown
LICENSE.gpl-v3
Notifications You must be signed in to change notification settings

joseaverde/Malef

Repository files navigation

Malef

Malef is a simple cross-compatible toolkit for writing terminal-based applications in Ada 2022. The library is completely written in Ada and has no dependencies.

This project's objective is to provide a simple and intuitive way of writting blazing fast Terminal User Interfaces (TUI). There is a lot of effort put into making API convenient and intuitive so the the developer doesn't neet to think about low-level stuff.

License: GPL v3 License: FDL 1.3

The library is in active development. The API is not definitive yet but it is usable

Description

Malef is an open source and free library written in Ada to create Terminal based User Interfaces. It divided in two parts: the Drawing Toolkit and the Widget Toolkit. The Drawing Toolkit provides the low-level stuff: mainly drawing and composition primitives. And the Widget Toolkit abstracts the way of building Applications using an interface similar to other G.U.I. toolkits such as Gtk or Qt using widgets.

The Drawing Toolkit

The Drawing Toolkit provides a low-level API for drawing Surfaces, composing them in Groups and showing them on the Window. If you want to create your own Widgets you will have to use this toolkit. See the packages under src/drawing.

Surfaces

A Surface is just a 2-dimensional Matrix of Glyphs (Wide_Wide_Characters), Colours and Styles. Drawing on them is very simple, you can take a look at the malef-surfaces.ads package to see all the available functions to draw. The functions added to that package are for convenience.

Also, some types have their own String Literals (new in Ada 2022). So you can specify a colour with an array or with an HTML colour tags.

with Malef.Surfaces;

procedure Surface_Example is
   My_Surface : Malef.Surfaces.Surface (3, 12);   -- Specify the number of
                                                  -- Rows and Columns.

begin

   My_Surface (1, 1) := 'H';
   My_Surface.Put (1, 2, "ello, World");
   My_Surface.Fill_Background ("#507878");
   My_Surface.Fill_Foreground ((1, 1), (1, 12), "#CCC");
   My_Surface.Fill ((1, 1), (1, 5), (Bold => True, others => False));

end Surface_Example;

Surfaces may have transparency, colours are represented as RGBA values (Red, Green, Blue and Alpha), where Alpha is the opacity of the colour (255 is completely opaque and 0 is completely transparent).

Groups

A Group is a data-structure that may contain different Surfaces or even other Groups. It is similar to the Groups of Layers you can create when you work with GIMP.


                  _________________
Bottom Layer .   /,,,,,,,,,,,,,,,,/| <----------- Top Layer
             |  /________________/ |
             V _|_|___________   | |
              /.|.|......... /|  | |
         ____/..|.|........ /_|__|_|______
        /   /...|......... /  |  | |     /
       /   /....|........ /___|  | |    /
      /   /______________/ .. /  | |   /
     /    |   /.|........|.../___|_|  /
    /     |  /..|;;;;;;;;|;;,,,,,|,/ /    <----- Group Base Layer
   /      | /............|.______|/ /
  /       |/_____________|/        /
 /                                /
/________________________________/

Each Layer has a opacity associated with it and a Layer_Mode (the function to be used in order to mix a layer with the layer below).

Groups are aggregates you can define your groups just like:

with Malef.Surfaces; use Malef.Surfaces;
with Malef.Groups;   use Malef.Groups;

procedure Group_Example is
   Red      : Surface (10, 10);
   My_Group : Group (6) := [Layer (Red, Opacity => 0.5),  -- Opacity
                            Layer (Red, Hidden => True),  -- Hidden?
                            Layer (Red, (2, 2)),          -- Position
                            No_Layer,                     -- Leave free space
                            Layer (20, 10),               -- Create a surface
                                                          -- of a given size.
                            Layer ([Layer (Red),          -- Add a group
                                    Layer (Red),
                                    Layer (Red)]
                                    (10, 20),
                                    Opacity => 0.9)];
   -- When adding a Surface to a Group a copy is always made, so changing the
   -- value of the `Red` surface won't change the contents of the group.
   -- Instead, use the `renames` directive to take a reference of the surface.
   -- Tampering rules will be running while you have a reference, so the group
   -- cannot be modified while it is being referenced.
   My_Surface renames Group (1).Set_Surface.Element;     -- Can be modified
   Surface_View renames Group (1).Get_Surface.Element;   -- Just a view

begin

   My_Surface.Fill_Background ("#F00");
   Red.Fill_Background ("#F00");
   My_Group.Insert (2, Red);  -- Error: Tampering because we have a reference.
                              -- If we didn't have a reference, it would be
                              -- correct.

end Group_Example;

Adding a new Layer to a group implies a copy. If you are adding a group to another group a deep copy is made. And it may kill performance. Instead use the Move function, it is similar to move semantics in C++. It takes ownership of the group and clears the other group so it can be moved inside another group.

with Malef.Surfaces; use Malef.Surfaces;
with Malef.Groups;   use Malef.Groups;

procedure Move_A_Group_Example is
   Red, Green, Blue : Surface (10, 10);
   Group_A : Group (3) := [Layer (Red), Layer (Green), Layer (Blue)];
   Group_B : Group (4) := [Layer (Red), Layer (Group_A), Layer (Green),
                           Layer (Blue)];    -- Group_A is copied
   Group_C : Group (4) := [Layer (Red), Move (Group_A), Layer (Green),
                           Layer (Blue)];    -- Group_A is moved and can no
                                             -- longer be used.
   Group_D : Group (2) := [Layer (Blue),
                           Layer ([Red, Green])]; -- The second group is copied
   Group_E : Group (2) := [Layer (Blue),
                           Move (Layer([Red, Green]))];  -- No copies

begin
   null;
end Move_A_Group_Example;

Window

The Window is a protected object that contains a single Group. You can assign callbacks to the Window using a subscription/notifier model, i.e., when an event occurs, all subscribers are notified of that event. This will be done in different tasks to avoid blocking the Window itself.

Let's see the first working example.

with Malef.Groups;
with Malef.System;
with Malef.Window;

procedure RGB_Window is
   use Malef.Groups;
   My_Group : Group := [Layer (10, 10, (1, 1), Opacity => 0.3),
                        Layer (10, 10, (1, 5), Opacity => 0.3),
                        Layer (10, 10, (5, 3), Opacity => 0.3)];
   Red   renames My_Group.Set_Surface (1).Element;
   Green renames My_Group.Set_Surface (2).Element;
   Blue  renames My_Group.Set_Surface (3).Element;
begin

   Malef.System.Initialize;      -- Initialize the subsystem

   Red.Fill_Background ("#FF0000");
   Green.Fill_Background ("#00FF00");
   Blue.Fill_Background ("#0000FF");

   Malef.Window.Window.Set_Group (My_Group);
   -- If you want to process the group as it is in a protected object you have
   -- to pass a function to the `Process_Group` procedure.
   Malef.Window.Window.Display;

   Malef.System.Finalize;        -- Finalize the subsystem

end RGB_Window;

Callbacks

System

The Malef.System package contains initialisation, finalisation and other functions about the underlying system. If the developer forgets to finalize the library or even if and exception is thrown, this packages makes sure to clean up the terminal.

The Widget Toolkit

TODO

You can see examples in the examples directory.

Compilation

System Status
Linux Linux
Windows Windows
Subsystem Status
ANSI ANSI
CMD CMD
Terminfo Terminfo

There are some problems with colours in the CMD subsystem, everything else works though. But the ANSI subsystem is still more robust.

Tests

TODO

System Status
Linux Linux
Windows Windows

How to build it?

IMPORTANT: It only compiles with GCC >=13.2.0

Malef uses Alire, you only have to do

alr with malef

On a project and you are ready to go. (If have to publish it first)

You can clone this repository and compile the examples:

cd examples

alr build

All examples will appear in the bin directory.

Licenses

This library is made available under the GPLv3 license.

About

Malef is a terminal/console-handling library written in Ada for TUI applications.

Topics

Resources

License

GPL-3.0, Unknown licenses found

Licenses found

GPL-3.0
LICENSE
Unknown
LICENSE.gpl-v3

Stars

Watchers

Forks

Packages

No packages published

Languages