Skip to content

A lightweight GUI Rust framework for quick desktop applications.

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE.txt
MIT
LICENSE-MIT.txt
Notifications You must be signed in to change notification settings

had2020/Last-Straw

Folders and files

NameName
Last commit message
Last commit date

Latest commit

ย 

History

144 Commits
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 
ย 

Repository files navigation

Laststraw

MSRV crates.io Downloads

Ideomatic Simple GUI framework with only Minifb and Freetype-sys

Showcase Projects

See Github for Up To Date Docs

Enjoyed the project? Leaving a Star means the world โญ๏ธ

How to run example project

firstly this libary depends on the developer installing freetype2

Installing C library freetype in package manager for ๐Ÿฆ€ Rust.

  • $ macos ๐ŸŽ

    brew install freetype
    
  • $ Linux ๐Ÿง

    sudo apt-get install libfreetype6-dev
    
  • $ Windows ๐ŸชŸ

    ๐Ÿ“ Note: you will likely need to use GNUWin32 to compile, or someother package manager like Choco! Read: https://github.com/PistonDevelopers/freetype-sys?tab=readme-ov-file for more

    Or with the Choco package manager.

    choco install freetype

Installing the Crate.

cargo add laststraw
use laststraw::*;

fn main() {
    let mut app = App::new(500, 500, "test"); // app variable, must be only a single one named app, which is mutable.

    asx!({ // runs every frame while, app.should_close == false.
        single_line_text(&mut app, position!(20.0, 20.0, 40.0), "Hello"); // shows Hello on the screen

        set_next_button(&mut app, position!(30.0, 30.0, 30.0)); // this code is layer dependent
        set_next_button_text(&mut app, "helq");
        button!({
            single_line_text(
                &mut app,
                position!(20.0, 20.0, 40.0),
                "You clicked the button",
            );
            println!("Button press logged!");
        });
    });

    println!("app closed after window code."); // last line of code, like in a normal rust program, EVEN if the user exited with exit button.
    // FAILS only if force exit by task manager alike program.
}

Make sure to add dependencies in Cargo.toml

cargo add freetype-rs@0.37.0
cargo add minifb@0.27.0
cargo add rusttype@0.9.3
cargo add quote@1.0.38
cargo add syn@2.0.92 --features full

How it works

Main Parts

  • App | stuct

firstly, I use the App stuct to hold all of are current app window's infomation, i.e, size height, and some more that were needed from the minifb framework. A variable must be set with the name app and the impl of new. Just like the code seen below.

let mut app = App::new(500, 500, "test");
  1. The First argument is window height.
  2. The second argument is window width.
  3. The last argument is an &str of name of the program and window.
  • asx! | proc macro

asx is similar in dioxus and react. Every frame it will print "app_loop". Code here is looped, here is where you can add UI elements like single_line_text();

asx!({ println!("app_loop") })

Any element must be declared inside the asx!

How is GUI layered

Gui elements are layered as they are declared, the lowest is at the top. For example multi_line_text(), would be furtherest to the back.

asx!({
  set_window_color(&mut app, "Obsidian"); // undermost layer

  let lines: Vec<&str> = vec!["Apple fruit", "Banana", "Cherry pie", "Oreos"];
  multi_line_text(&mut app, position!(100.0, 100.0, 50.0), 50.0, lines); // topmost layer
})

Position Struct

used for setting location of a element on the screen. Made up of X: Height, Y: Weight, and Scale.

You can make one the position macro as seen below, or just write out the stuct for readablity.

position!(20.0, 20.0, 40.0)

OR

Position { x: 10.0, y: 10.0, scale: 50.0, }

for readablity

Text Elements

if app variables are updated, then we need to own a mut borrow, &mut app. Other's only borrow ownership like checking for input_pressed().

  • single_line_text
  1. First argument is a declared App stuct.
  2. The second argument is a stuct called position, it is made up of X: Height, Y: Weight, and Scale, deciding were the ui is placed.
  3. The third argument is a &str, with the message, displayed on the string. This does not take user input, but only displays some text.
single_line_text(&mut app, position!(20.0, 20.0, 40.0), "You pressed space");
  • multi_line_text
  1. First argument is a declared App stuct.
  2. The second argument is a stuct called position, it is made up of X: Height, Y: Weight, and Scale, deciding were the ui is placed.
  3. The third argument, are the lines of text, each index a line, stored in a Vec<&str>
let lines: Vec<&str> = vec!["Apple fruit", "Banana", "Cherry pie", "Oreos"]; // every new line is a index
multi_line_text(&mut app, position!(100.0, 100.0, 50.0), 50.0, lines); // lines needs index in a Vec<&str>

Button Element

Proc Macro which only runs the code inside one clicked. Similar to asx!

button!({
  println!("button is pressed");
});

Interactable text

  1. First argument is the declared App stuct, from outside asx.
  2. Second is a macro declaring a position struct, Hight, Width, Scale.
  3. The text seen before any text is enter by user.
  4. Color from color table, refered to at end of docs, or laststraw_core.
  5. If the element would only accept single line, false, means single line only.
let texty: String = editable_lines(
    &mut app, // mut app stuct variable created by new impl on App.
    position!(100.0, 50.0, 50.0), // position struct created by macro.
    "enter:", // preluding text.
    "Blue", // color of the boarder.
    false, // if you wish for only single line input.
);

Setting the initial text in following/next Interactable text

  • for setting multi_lines
let lines = vec!["one", "two", "three"]; // example &str list, each line is an index
following_input_initial_lines(&mut app, lines);
  • for setting a single line
following_input_initial_text(&mut app, "just a line here"); // you can use both for just a line, but this is easier

Changing initial Interactable text

place before a Interactable text element to set initial text, which stays after clicking the element.

pub fn set_next_input_init_text(app: &mut App, init_text: &str) {
    if app.already_set_initial_text == true {
        app.multi_line_storing[app.current_text_edit_id][1] = init_text.to_string();
    }
}

Input checking

  1. First argument is a declared App stuct.
  2. Second is the key see input table at the end of the docs or laststraw_core.
  3. Any code inside the if state behaves as expected, as the function returns true or false based off the if the key is pressed.
  if input_pressed(&app, "space") {
      single_line_text(&mut app, position!(20.0, 20.0, 40.0), "Hello");
  }

Changing the Window color

TIP make sure this is the futherest back, because it writes over the whole screen buffer. Sets the color of the background.

set_window_color(&mut app, "Obsidian");

Limit fps to reduce CPU usage if needed

  1. First argument is the declared App stuct, from outside asx.
  2. Second argument is the max frames per a second.

Note: this is optional, and used to reduce CPU usage for much heavy apps, with many elements.

limit_fps(&mut app, 60.0); // a nice 60 frames per a second, 30-60 is a good number.

Closing app

  • This framework uses a lot of abstraction from minifb and freetype.
  • So to simply close the app, set app.should_close to true.
  • This is becasue, asx is a avarge while loop like seen in many window frameworks like minifb: in this project, and Glfw.
  app.should_close = true; // closes app at line.

Structuring, Base Plate

This is a example of a base plate for a app, in this framework.

use laststraw::*;

fn main() {
    let mut app = App::new(500, 500, "test"); // app variable, must be only a single one named app, which is mutable.

    asx!({ // runs every frame while, app.should_close == false.
        single_line_text(&mut app, position!(20.0, 20.0, 40.0), "Hello"); // shows Hello on the screen

        if input_pressed(&app, "esc") { // if the esc key is pressed the inside code runs.
            app.should_close = true; // stops the asx loop to end.
        }
    });

    println!("app closed after window code."); // last line of code, like in a normal rust program, EVEN if the user exited with exit button.
    // FAILS only if force exit by task manager alike program.
}

๐Ÿ”‘ Key Table

๐Ÿ”ฃ Symbol ๐Ÿ–ฎ Key Mapping
โŽ‹ esc Key::Escape
1๏ธโƒฃ 1 Key::Key1
2๏ธโƒฃ 2 Key::Key2
3๏ธโƒฃ 3 Key::Key3
4๏ธโƒฃ 4 Key::Key4
5๏ธโƒฃ 5 Key::Key5
6๏ธโƒฃ 6 Key::Key6
7๏ธโƒฃ 7 Key::Key7
8๏ธโƒฃ 8 Key::Key8
9๏ธโƒฃ 9 Key::Key9
0๏ธโƒฃ 0 Key::Key0
๐Ÿ…ฐ๏ธ a Key::A
๐Ÿ…ฑ๏ธ b Key::B
๐Ÿ‡จ c Key::C
๐Ÿ‡ฉ d Key::D
๐Ÿ‡ช e Key::E
๐Ÿ‡ซ f Key::F
๐Ÿ‡ฌ g Key::G
๐Ÿ‡ญ h Key::H
๐Ÿ‡ฎ i Key::I
๐Ÿ‡ฏ j Key::J
๐Ÿ‡ฐ k Key::K
๐Ÿ‡ฑ l Key::L
๐Ÿ‡ฒ m Key::M
๐Ÿ‡ณ n Key::N
๐Ÿ‡ด o Key::O
๐Ÿ…ฟ๏ธ p Key::P
๐Ÿ‡ถ q Key::Q
๐Ÿ‡ท r Key::R
๐Ÿ‡ธ s Key::S
๐Ÿ‡น t Key::T
๐Ÿ‡บ u Key::U
๐Ÿ‡ป v Key::V
๐Ÿ‡ผ w Key::W
โŽ x Key::X
๐Ÿ‡พ y Key::Y
๐Ÿ‡ฟ z Key::Z
โฃ space Key::Space
โ†ต enter Key::Enter
โ‡ฅ tab Key::Tab
โŒซ backspace Key::Backspace
โ—€๏ธ left Key::Left
โ–ถ๏ธ right Key::Right
๐Ÿ”ผ up Key::Up
๐Ÿ”ฝ down Key::Down
โ‡ง left_shift Key::LeftShift
โ‡ง right_shift Key::RightShift
โŒƒ left_ctrl Key::LeftCtrl
โŒƒ right_ctrl Key::RightCtrl
โއ left_alt Key::LeftAlt
โއ right_alt Key::RightAlt
๐Ÿ”ข ; Key::Semicolon
๐Ÿ–‹๏ธ ' Key::Apostrophe
๐Ÿฅ’ , Key::Comma
. Key::Period
โž— / Key::Slash
โฌ› backslash Key::Backslash
๐Ÿ—‚๏ธ left_bracket Key::LeftBracket
๐Ÿ“‚ right_bracket Key::RightBracket
โž– - Key::Minus
โž• + Key::Equal
๐Ÿ”’ caps_lock Key::CapsLock
๐Ÿ›‘ scroll_lock Key::ScrollLock
๐Ÿ”ข num_lock Key::NumLock
โธ๏ธ pause Key::Pause
๐Ÿ–‹๏ธ insert Key::Insert
๐Ÿ  home Key::Home
๐Ÿ“„ page_up Key::PageUp
๐Ÿ—‘๏ธ delete Key::Delete
๐Ÿ end Key::End
๐Ÿ“‰ page_down Key::PageDown
๐Ÿ”ˆ f1 Key::F1
๐ŸŽ›๏ธ f2 Key::F2
๐Ÿ”ข f3 Key::F3
๐Ÿ”ข f4 Key::F4
๐Ÿ”ข f5 Key::F5
๐Ÿ”ข f6 Key::F6
๐Ÿ”ข f7 Key::F7
๐Ÿ”ข f8 Key::F8
๐Ÿ”ข f9 Key::F9
๐Ÿ”ข f10 Key::F10
๐Ÿ”ข f11 Key::F11
๐Ÿ”ข f12 Key::F12

๐ŸŽจ Color Table

Color Name Hex Code
Green ๐ŸŸข 0xFF00FF00
Red ๐Ÿ”ด 0xFFFF0000
Blue ๐Ÿ”ต 0xFF0000FF
Yellow ๐ŸŸก 0xFFFFFF00
Cyan ๐ŸŸฆ 0xFF00FFFF
Magenta ๐Ÿ’– 0xFFFF00FF
White โšช 0xFFFFFFFF
Black โšซ 0xFF000000
Gray ๐Ÿฉถ 0xFF808080
Orange ๐ŸŸ  0xFFFFA500
Purple ๐ŸŸฃ 0xFF800080
Pink ๐ŸŒธ 0xFFFFC0CB
Brown ๐ŸŸค 0xFFA52A2A
Light Gray ๐Ÿชถ 0xFFD3D3D3
Light Blue ๐ŸŒŠ 0xFFADD8E6
Dark Blue ๐Ÿดโ€โ˜ ๏ธ 0xFF00008B
Beige ๐ŸคŽ 0xFFF5F5DC
Teal ๐Ÿ  0xFF008080
Lavender ๐ŸŒพ 0xFFE6E6FA
Ivory ๐Ÿ›๏ธ 0xFFFFFFF0
Mint ๐ŸŒฟ 0xFF98FF98
Coral ๐Ÿš 0xFFFF7F50
Navy โš“ 0xFF000080
Sky Blue โ˜๏ธ 0xFF87CEEB
Sea Green ๐ŸŒŠ 0xFF2E8B57
Forest Green ๐ŸŒฒ 0xFF228B22
Dark Gray ๐Ÿ–ค 0xFFA9A9A9
Slate Gray ๐Ÿ”๏ธ 0xFF708090
Charcoal ๐Ÿญ 0xFF36454F
Jet Black ๐Ÿ•ถ๏ธ 0xFF343434
Gunmetal ๐Ÿ”ซ 0xFF2A3439
Dark Slate Blue ๐ŸŒŒ 0xFF483D8B
Midnight Blue ๐ŸŒƒ 0xFF191970
Deep Navy ๐Ÿ›ณ๏ธ 0xFF1B1F3B
Dark Olive Green ๐ŸŒฟ 0xFF556B2F
Deep Forest Green ๐ŸŒณ 0xFF1A2E1A
Maroon ๐Ÿท 0xFF800000
Deep Burgundy ๐Ÿ‡ 0xFF4A0000
Dark Chocolate ๐Ÿซ 0xFF3E2723
Dark Copper ๐Ÿบ 0xFF4E3629
Onyx ๐Ÿ’Ž 0xFF353839
Obsidian ๐ŸŒ‹ 0xFF1C1C1C
Default (Invalid) 0xFFFFC0CB

Example project, Element showcase

This app code showcases all the elements in use, you can refer to it when using this framework.

use laststraw::*;

fn main() {
    let mut app = App::new(500, 500, "test");

    asx!({
        set_window_color(&mut app, "Obsidian"); // is layored so this is back

        if input_pressed(&app, "esc") {
            app.should_close = true;
        }

        if input_pressed(&app, "space") {
            single_line_text(&mut app, position!(20.0, 20.0, 40.0), "You pressed space");
        }

        let lines: Vec<&str> = vec!["Apple fruit", "Banana", "Cherry pie", "Oreos"];
        multi_line_text(&mut app, position!(100.0, 100.0, 50.0), 50.0, lines);

        set_next_button(&mut app, position!(30.0, 30.0, 30.0));
        set_next_button_text(&mut app, "helq");
        button!({
            single_line_text(
                &mut app,
                position!(20.0, 20.0, 40.0),
                "You clicked the button",
            );
        });

        let texty = editable_lines(
            &mut app,
            position!(100.0, 50.0, 50.0),
            "enter:",
            "Blue",
            false,
        );
        single_line_text(&mut app, position!(20.0, 20.0, 40.0), &texty); // you can acess the value later, will be empty, never recived input

        limit_fps(&mut app, 60.0);
    });

    println!("app closed after window code.");
}

Why / Project statement ๐Ÿ“

I took lots of inspiration from Rust Frameworks like Dioxus and Tauri Frameworks. The main problem for my with these frameworks, is that desktop apps, acted like mini web browsers.

This meant I had to tailer my code to not interact with the hardware, like a website, with static files. I just needed a framework that had buttons and multiline text to enter and display. Just something to make a basic text editer.

I like the features of Iced, but I wanted a more light CPU, based framework. That could also be cross compatible. I also wanted easy to read variable names, as once someone learns them, they can just macrofi them.

I also wished it to be easyer for people new to Rust to be able to make Apps. As I belive people like projects they can see rendered on their screen more.

Hopfully, this project makes some dev happy to keep their Rust app low level.

Contributing ๐Ÿ’ช

Since, this is my first published crate framework, I expect their might be bugs. Feel free to open an issue or anything, if you belive you can do the code better. I would love to learn any new ideas, to make this framework better!

About

A lightweight GUI Rust framework for quick desktop applications.

Resources

License

Apache-2.0, MIT licenses found

Licenses found

Apache-2.0
LICENSE-APACHE.txt
MIT
LICENSE-MIT.txt

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages