From 070f864d0411266bd79e16582f08bc426a4090b4 Mon Sep 17 00:00:00 2001 From: "Artem V. Ageev" Date: Sun, 31 Mar 2024 21:35:34 +0300 Subject: [PATCH] add workspace --- Cargo.toml | 46 ++++ README.md | 63 +++--- cairo-demo/Cargo.toml | 2 +- cairo_shadow_button/Cargo.toml | 4 +- calendar/Cargo.toml | 4 +- calendar/src/calendar.rs | 9 +- egui-demo/src/main.rs | 7 +- ffmpeg/Cargo.toml | 2 +- {csv => flcsv}/Cargo.toml | 6 +- {csv => flcsv}/README.md | 0 {csv => flcsv}/csv.gif | Bin {csv => flcsv}/ex.jpg | Bin {csv => flcsv}/historical_data/GME.csv | 0 {csv => flcsv}/historical_data/dlpn.csv | 0 {csv => flcsv}/historical_data/oil.csv | 0 flcsv/image.jpg | Bin 0 -> 22609 bytes {csv => flcsv}/src/main.rs | 8 +- {femtovg => flfemtovg}/Cargo.toml | 4 +- {femtovg => flfemtovg}/README.md | 0 {femtovg => flfemtovg}/ex.png | Bin {femtovg => flfemtovg}/femtovg.gif | Bin {femtovg => flfemtovg}/src/main.rs | 0 {glium => flglium}/Cargo.toml | 4 +- {glium => flglium}/README.md | 0 {glium => flglium}/ex.jpg | Bin {glium => flglium}/glium.gif | Bin {glium => flglium}/src/main.rs | 0 {glow => flglow}/Cargo.toml | 6 +- {glow => flglow}/README.md | 0 {glow => flglow}/ex.jpg | Bin {glow => flglow}/glow.gif | Bin {glow => flglow}/src/main.rs | 0 {image => flimage}/Cargo.toml | 4 +- {image => flimage}/README.md | 0 {image => flimage}/fltk.png | Bin {image => flimage}/src/main.rs | 0 {libmpv => fllibmpv}/Cargo.toml | 4 +- {libmpv => fllibmpv}/README.md | 0 {libmpv => fllibmpv}/src/main.rs | 2 +- {libmpv => fllibmpv}/src/sys_main.rs | 0 {pixels => flpixels}/Cargo.toml | 6 +- {pixels => flpixels}/README.md | 0 {pixels => flpixels}/build.rs | 0 {pixels => flpixels}/ex.jpg | Bin {pixels => flpixels}/pixels.gif | Bin {pixels => flpixels}/src/main.rs | 0 {plotters => flplotters}/Cargo.toml | 4 +- {plotters => flplotters}/README.md | 0 {plotters => flplotters}/ex.jpg | Bin {plotters => flplotters}/plotters.gif | Bin {plotters => flplotters}/src/main.rs | 0 {raqote => flraqote}/Cargo.toml | 6 +- {raqote => flraqote}/README.md | 0 {raqote => flraqote}/ex.jpg | Bin {raqote => flraqote}/raqote.gif | Bin {raqote => flraqote}/src/main.rs | 4 +- {speedy2d => flspeedy2d}/Cargo.toml | 2 +- {speedy2d => flspeedy2d}/README.md | 0 {speedy2d => flspeedy2d}/ex.jpg | Bin {speedy2d => flspeedy2d}/speedy2d.gif | Bin {speedy2d => flspeedy2d}/src/main.rs | 0 fltext/Cargo.toml | 33 +++ fltext/LICENSE | 21 ++ fltext/README.md | 34 +++ fltext/src/cbs.rs | 222 +++++++++++++++++++ fltext/src/dialogs.rs | 199 ++++++++++++++++++ fltext/src/fbr.rs | 128 +++++++++++ fltext/src/gui.rs | 269 ++++++++++++++++++++++++ fltext/src/highlight/colors.rs | 9 + fltext/src/highlight/md.rs | 23 ++ fltext/src/highlight/mod.rs | 130 ++++++++++++ fltext/src/highlight/rust.rs | 36 ++++ fltext/src/highlight/toml.rs | 24 +++ fltext/src/main.rs | 18 ++ fltext/src/state.rs | 143 +++++++++++++ fltext/src/utils.rs | 63 ++++++ {wgpu => flwgpu}/Cargo.toml | 8 +- {wgpu => flwgpu}/README.md | 0 {wgpu => flwgpu}/src/main.rs | 0 {wgpu => flwgpu}/src/shader.wgsl | 0 {wgpu => flwgpu}/wgpu.gif | Bin framebuffer/Cargo.toml | 2 +- framebuffer/src/main.rs | 1 - glut/Cargo.toml | 4 +- glut/src/main.rs | 29 +-- glyphmap/Cargo.toml | 4 +- glyphmap/src/main.rs | 7 +- gst/Cargo.toml | 4 +- gst/src/main.rs | 2 +- libvlc/Cargo.toml | 2 +- libvlc/src/main.rs | 21 +- mpv/Cargo.toml | 2 +- musicplayer/Cargo.toml | 4 +- musicplayer/src/main.rs | 3 +- musicplayer/src/power_button.rs | 12 +- opengl/Cargo.toml | 2 +- opengl/src/main.rs | 6 +- rounded-svg/Cargo.toml | 4 +- systray/Cargo.toml | 4 +- systray/src/main.rs | 4 +- systray/src/systray.rs | 2 +- terminal/Cargo.toml | 4 +- tinyskia/Cargo.toml | 4 +- tinyskia/src/main.rs | 20 +- web-todo/Cargo.toml | 2 +- web-todo2/Cargo.toml | 2 +- webview/Cargo.toml | 4 +- webview/src/main.rs | 7 +- xterm/Cargo.toml | 2 +- xterm/src/main.rs | 3 +- 110 files changed, 1542 insertions(+), 182 deletions(-) create mode 100644 Cargo.toml rename {csv => flcsv}/Cargo.toml (89%) rename {csv => flcsv}/README.md (100%) rename {csv => flcsv}/csv.gif (100%) rename {csv => flcsv}/ex.jpg (100%) rename {csv => flcsv}/historical_data/GME.csv (100%) rename {csv => flcsv}/historical_data/dlpn.csv (100%) rename {csv => flcsv}/historical_data/oil.csv (100%) create mode 100644 flcsv/image.jpg rename {csv => flcsv}/src/main.rs (95%) rename {femtovg => flfemtovg}/Cargo.toml (74%) rename {femtovg => flfemtovg}/README.md (100%) rename {femtovg => flfemtovg}/ex.png (100%) rename {femtovg => flfemtovg}/femtovg.gif (100%) rename {femtovg => flfemtovg}/src/main.rs (100%) rename {glium => flglium}/Cargo.toml (79%) rename {glium => flglium}/README.md (100%) rename {glium => flglium}/ex.jpg (100%) rename {glium => flglium}/glium.gif (100%) rename {glium => flglium}/src/main.rs (100%) rename {glow => flglow}/Cargo.toml (69%) rename {glow => flglow}/README.md (100%) rename {glow => flglow}/ex.jpg (100%) rename {glow => flglow}/glow.gif (100%) rename {glow => flglow}/src/main.rs (100%) rename {image => flimage}/Cargo.toml (88%) rename {image => flimage}/README.md (100%) rename {image => flimage}/fltk.png (100%) rename {image => flimage}/src/main.rs (100%) rename {libmpv => fllibmpv}/Cargo.toml (76%) rename {libmpv => fllibmpv}/README.md (100%) rename {libmpv => fllibmpv}/src/main.rs (98%) rename {libmpv => fllibmpv}/src/sys_main.rs (100%) rename {pixels => flpixels}/Cargo.toml (70%) rename {pixels => flpixels}/README.md (100%) rename {pixels => flpixels}/build.rs (100%) rename {pixels => flpixels}/ex.jpg (100%) rename {pixels => flpixels}/pixels.gif (100%) rename {pixels => flpixels}/src/main.rs (100%) rename {plotters => flplotters}/Cargo.toml (88%) rename {plotters => flplotters}/README.md (100%) rename {plotters => flplotters}/ex.jpg (100%) rename {plotters => flplotters}/plotters.gif (100%) rename {plotters => flplotters}/src/main.rs (100%) rename {raqote => flraqote}/Cargo.toml (82%) rename {raqote => flraqote}/README.md (100%) rename {raqote => flraqote}/ex.jpg (100%) rename {raqote => flraqote}/raqote.gif (100%) rename {raqote => flraqote}/src/main.rs (97%) rename {speedy2d => flspeedy2d}/Cargo.toml (83%) rename {speedy2d => flspeedy2d}/README.md (100%) rename {speedy2d => flspeedy2d}/ex.jpg (100%) rename {speedy2d => flspeedy2d}/speedy2d.gif (100%) rename {speedy2d => flspeedy2d}/src/main.rs (100%) create mode 100644 fltext/Cargo.toml create mode 100644 fltext/LICENSE create mode 100644 fltext/README.md create mode 100644 fltext/src/cbs.rs create mode 100644 fltext/src/dialogs.rs create mode 100644 fltext/src/fbr.rs create mode 100644 fltext/src/gui.rs create mode 100644 fltext/src/highlight/colors.rs create mode 100644 fltext/src/highlight/md.rs create mode 100644 fltext/src/highlight/mod.rs create mode 100644 fltext/src/highlight/rust.rs create mode 100644 fltext/src/highlight/toml.rs create mode 100644 fltext/src/main.rs create mode 100644 fltext/src/state.rs create mode 100644 fltext/src/utils.rs rename {wgpu => flwgpu}/Cargo.toml (68%) rename {wgpu => flwgpu}/README.md (100%) rename {wgpu => flwgpu}/src/main.rs (100%) rename {wgpu => flwgpu}/src/shader.wgsl (100%) rename {wgpu => flwgpu}/wgpu.gif (100%) diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..26f1b69 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,46 @@ +[workspace] + +members = [ + "cairo-demo", + "cairo_shadow_button", + "calendar", + "egui-demo", + "ffmpeg", + "flcalculator", + "flcsv", + "fldialect", + "flfemtovg", + "flglium", + "flglow", + "flimage", + "fllibmpv", + "flpixels", + "flplotters", + "flraqote", + "flspeedy2d", + "flresters", + "fltext", + "flwgpu", + "framebuffer", + "glut", + "glyphmap", + "gst", + "libvlc", + "mpv", + "musicplayer", + "opengl", + "rounded-svg", + "systray", + "terminal", + "tinyskia", + "web-todo", + "web-todo2", + "webview", + "xterm", +] + +[profile.release] +strip = true +opt-level = "z" +lto = true +panic = "abort" diff --git a/README.md b/README.md index b112b82..e0a8705 100644 --- a/README.md +++ b/README.md @@ -64,38 +64,37 @@ This repository is licensed under the MIT license. You can get more information --- The current demos include: -- ๐ŸŒโ€Šweb-todo: Creating an async web todo app using fltk, reqwest, serde and tokio. -- ๐ŸŒโ€Šweb-todo2: Creating an async web todo app using fltk, surf, serde and async-std. -- ๐Ÿ“บโ€Šlibvlc: Creating a media player using fltk and the vlc crate. -- ๐ŸŽถ musicplayer: Creating a music player using custom widgets and the soloud crate. -- ๐ŸŽจโ€Šopengl: Raw OpenGL drawing in an fltk GlWindow. -- ๐Ÿ–Œ๏ธโ€Šglut: Use the gl crate (An OpenGL function pointer loader) to do OpenGL drawing. -- ๐Ÿ–Š๏ธ wgpu: Use wgpu-rs for gpu accelerated drawing. -- ๐ŸŽž๏ธ pixels: Use the pixels crate to draw a wgpu accelerated framebuffer. -- โœ’๏ธ framebuffer: Using fltk for framebuffer drawing. -- ๐ŸŒŒ plotters: Use plotters for live plotting (drawing animations) with fltk. -- ๐ŸŒˆโ€Šraqote: Use raqote for custom drawing (paint example). -- ๐Ÿ–ผ๏ธโ€Štinyskia: Use tiny-skia for custom drawing. -- ๐Ÿ–ฅ๏ธ systray: Use nwg to create an fltk app with systray functionalities on Windows -- โœจโ€Šglow: Use the glow crate to do OpengGL drawing. -- ๐ŸŽ‡ glium: Use the glium crate for OpenGL drawing. -- ๐Ÿ—“๏ธโ€Šcalendar: Uses the chrono crate to create an fltk calendar dialog. -- ๐Ÿž๏ธ image: Uses rust-embed and the image crates to load images into fltk. -- ๐ŸŒš speedy2d: Uses speedy2D crate to do 2D drawings of a circle and an RGB image in a GlWindow. -- ๐Ÿช femtovg: Uses femtovg for 2D drawing in a GlWindow. -- ๐Ÿ“ฝ๏ธ ffmpeg: Uses ffmpeg for software video rendering. -- ๐Ÿ’ป webview: Embeds a webview inside an fltk app. -- ๐Ÿ–๏ธโ€Šcsv: Uses serde and csv to perform custom drawing of data. -- ๐Ÿ”˜ rounded-svg: Use the svg crate along with fltk to create images with rounded borders. -- ๐Ÿ”ฆโ€Šlibmpv: use libmpv to play a video inside an fltk GlWindow. -- โ€Š๐Ÿงฅโ€Šmpv: mpv: Use mpv (the command line app) to play a video inside an fltk window. -- ๐Ÿ“ฒโ€Šxterm: embed an xterm window inside an fltk window. -- ๐ŸŽ›๏ธ egui-demo: Use fltk as a backend for egui -- ๐ŸŽž๏ธ gst: Use libgstreamer to play a video inside an fltk window -- ๐Ÿœ๏ธ cairo-demo: Use cairo for custom drawing inside fltk widgets -- ๐Ÿ“โ€Šglyphmap: Maps glyphs (specifically font icons) to their unicode codepoint. +* [๐ŸŒโ€Š web-todo:]("#web-todo") Creating an async web todo app using fltk, reqwest, serde and tokio. +* [๐ŸŒโ€Š web-todo2:](#web-todo2) Creating an async web todo app using fltk, surf, serde and async-std. +* [๐Ÿ“บโ€Š libvlc:](#libvlc)" Creating a media player using fltk and the vlc crate. +* [๐ŸŽถ musicplayer:](#musicplayer) Creating a music player using custom widgets and the soloud crate. +* [๐ŸŽจโ€Š opengl:](#opengl) Raw OpenGL drawing in an fltk GlWindow. +* [๐Ÿ–Œ๏ธโ€Š glut:](#glut) Use the gl crate (An OpenGL function pointer loader) to do OpenGL drawing. +* [๐Ÿ–Š๏ธ flwgpu:](#flwgpu) Use wgpu-rs for gpu accelerated drawing. +* [๐ŸŽž๏ธ flpixels:](#flpixels) Use the pixels crate to draw a wgpu accelerated framebuffer. +* [โœ’๏ธ framebuffer:](#framebuffer) Using fltk for framebuffer drawing. +* [๐ŸŒŒ flplotters:](#flplotters) Use plotters for live plotting (drawing animations) with fltk. +* [๐ŸŒˆโ€Š flraqote:](#flraqote) Use raqote for custom drawing (paint example). +* [๐Ÿ–ผ๏ธโ€Š tinyskia:](#tinyskia) Use tiny-skia for custom drawing. +* [๐Ÿ–ฅ๏ธ systray:](#systray) Use nwg to create an fltk app with systray functionalities on Windows +* [โœจ flโ€Šglow:](#flglow) Use the glow crate to do OpengGL drawing. +* [๐ŸŽ‡ flglium:](#flglium) Use the glium crate for OpenGL drawing. +* [๐Ÿ—“๏ธโ€Š calendar:](#calendar) Uses the chrono crate to create an fltk calendar dialog. +* [๐Ÿž๏ธ flimage:](#flimage) Uses rust-embed and the image crates to load images into fltk. +* [๐ŸŒš flspeedy2d:](#flspeedy2d) Uses speedy2D crate to do 2D drawings of a circle and an RGB image in a GlWindow. +* [๐Ÿช flfemtovg:](#flfemtovg) Uses femtovg for 2D drawing in a GlWindow. +* [๐Ÿ“ฝ๏ธ ffmpeg:](#ffmpeg) Uses ffmpeg for software video rendering. +* [๐Ÿ’ป webview:](#webview) Embeds a webview inside an fltk app. +* [๐Ÿ–๏ธโ€Š flcsv:](#flcsv) Uses serde and csv to perform custom drawing of data. +* [๐Ÿ”˜ rounded-svg:](#rounded-svg) Use the svg crate along with fltk to create images with rounded borders. +* [๐Ÿ”ฆโ€Š fllibmpv:](#libmpv) use libmpv to play a video inside an fltk GlWindow. +* [โ€Š๐Ÿงฅโ€Š mpv:](#mpv) Use mpv (the command line app) to play a video inside an fltk window. +* [๐Ÿ“ฒโ€Šxterm:](#xterm) embed an xterm window inside an fltk window. +* [๐ŸŽ›๏ธ egui-demo:](#egui-demo) Use fltk as a backend for egui +* [๐ŸŽž๏ธ gst:](#gst) Use libgstreamer to play a video inside an fltk window +* [๐Ÿœ๏ธ cairo-demo:](#cairo-demo) Use cairo for custom drawing inside fltk widgets +* [๐Ÿ“โ€Šglyphmap:](#glyphmap) Maps glyphs (specifically font icons) to their unicode codepoint. - ๐Ÿ“Ÿ terminal: A minimal terminal emulator. - --- @@ -195,4 +194,4 @@ The current demos include: ![cairo](cairo_shadow_button/ex1.jpg) - \ No newline at end of file + diff --git a/cairo-demo/Cargo.toml b/cairo-demo/Cargo.toml index 1395397..2aa6e90 100644 --- a/cairo-demo/Cargo.toml +++ b/cairo-demo/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = { version = "1.4.9", features = ["cairoext"] } +fltk = { version = "^1.4", features = ["cairoext"] } cairo-rs = "0.18" diff --git a/cairo_shadow_button/Cargo.toml b/cairo_shadow_button/Cargo.toml index 3c086b5..86d04ed 100644 --- a/cairo_shadow_button/Cargo.toml +++ b/cairo_shadow_button/Cargo.toml @@ -4,6 +4,6 @@ version = "0.1.0" edition = "2021" [dependencies] -fltk = "1.4" +fltk = "^1.4" cairo-rs = "0.18" -cairo-blur = "0.1.0" \ No newline at end of file +cairo-blur = "0.1.0" diff --git a/calendar/Cargo.toml b/calendar/Cargo.toml index 84c9244..b12f6ea 100644 --- a/calendar/Cargo.toml +++ b/calendar/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1" -chrono = "0.4" \ No newline at end of file +fltk = "^1.4" +chrono = "0.4" diff --git a/calendar/src/calendar.rs b/calendar/src/calendar.rs index 6e69183..c6e0ac7 100644 --- a/calendar/src/calendar.rs +++ b/calendar/src/calendar.rs @@ -1,7 +1,4 @@ -use chrono::DateTime; -use chrono::Datelike; -use chrono::Local; -use chrono::NaiveDate; +use chrono::{DateTime, Datelike, Local, NaiveDate}; use fltk::{app, draw, enums::*, menu, prelude::*, table, window}; use std::{cell::RefCell, rc::Rc}; @@ -93,14 +90,14 @@ impl Calendar { _ => (), }); - let curr_rc = curr.clone(); + let curr_rc = curr; // redraw table when the month changes month_choice.set_callback(move |c| { *curr_rc.borrow_mut() = c.value() + 1; c.parent().unwrap().redraw(); }); - let curr_year_rc = curr_year.clone(); + let curr_year_rc = curr_year; // redraw table when the year changes year_choice.set_callback(move |c| { *curr_year_rc.borrow_mut() = c.value() + 1900; diff --git a/egui-demo/src/main.rs b/egui-demo/src/main.rs index acfa0f7..4ef0d8a 100644 --- a/egui-demo/src/main.rs +++ b/egui-demo/src/main.rs @@ -4,8 +4,7 @@ use egui_backend::{ gl, DpiScaling, }; use fltk_egui as egui_backend; -use std::rc::Rc; -use std::{cell::RefCell, time::Instant}; +use std::{cell::RefCell, rc::Rc, time::Instant}; const SCREEN_WIDTH: u32 = 800; const SCREEN_HEIGHT: u32 = 600; @@ -31,7 +30,7 @@ fn main() { slider.set_slider_size(0.20); slider.set_color(Color::Blue.inactive()); slider.set_selection_color(Color::Red); - col.set_size(&mut slider, 20); + col.fixed(&slider, 20); col.end(); main_win.end(); main_win.make_resizable(true); @@ -111,7 +110,7 @@ fn main() { let paint_jobs = egui_ctx.tessellate(paint_cmds); //Draw egui texture - painter.paint_jobs(None, paint_jobs, &egui_ctx.texture()); + painter.paint_jobs(None, paint_jobs, &egui_ctx.font_image()); glut_win.swap_buffers(); glut_win.flush(); diff --git a/ffmpeg/Cargo.toml b/ffmpeg/Cargo.toml index 3ac1607..e6d5d50 100644 --- a/ffmpeg/Cargo.toml +++ b/ffmpeg/Cargo.toml @@ -7,6 +7,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1" +fltk = "^1.4" signal-hook = "0.3" lazy_static = "1.4" diff --git a/csv/Cargo.toml b/flcsv/Cargo.toml similarity index 89% rename from csv/Cargo.toml rename to flcsv/Cargo.toml index 1a1c4e3..0d38aa2 100644 --- a/csv/Cargo.toml +++ b/flcsv/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "csv" +name = "flcsv" version = "0.1.0" authors = ["MoAlyousef "] edition = "2021" @@ -7,8 +7,8 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1" +fltk = "^1.4" csv = "1.1.6" serde = { version = "1", features = ["derive"] } lazy_static = "1.4" -image = { version = "0.24.5", default-features = false, features = ["jpeg"] } \ No newline at end of file +image = { version = "0.24.5", default-features = false, features = ["jpeg"] } diff --git a/csv/README.md b/flcsv/README.md similarity index 100% rename from csv/README.md rename to flcsv/README.md diff --git a/csv/csv.gif b/flcsv/csv.gif similarity index 100% rename from csv/csv.gif rename to flcsv/csv.gif diff --git a/csv/ex.jpg b/flcsv/ex.jpg similarity index 100% rename from csv/ex.jpg rename to flcsv/ex.jpg diff --git a/csv/historical_data/GME.csv b/flcsv/historical_data/GME.csv similarity index 100% rename from csv/historical_data/GME.csv rename to flcsv/historical_data/GME.csv diff --git a/csv/historical_data/dlpn.csv b/flcsv/historical_data/dlpn.csv similarity index 100% rename from csv/historical_data/dlpn.csv rename to flcsv/historical_data/dlpn.csv diff --git a/csv/historical_data/oil.csv b/flcsv/historical_data/oil.csv similarity index 100% rename from csv/historical_data/oil.csv rename to flcsv/historical_data/oil.csv diff --git a/flcsv/image.jpg b/flcsv/image.jpg new file mode 100644 index 0000000000000000000000000000000000000000..7892e615655e0eb724c406f5f2c84d1978d441a1 GIT binary patch literal 22609 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 .iter() .cloned() - .fold(0. / 0., f64::max); + .fold(f64::NAN, f64::max); highest += (highest.to_string().len() * 10) as f64 / 3.; let factor = f.h() as f64 / highest; if data.len() != 0 { diff --git a/femtovg/Cargo.toml b/flfemtovg/Cargo.toml similarity index 74% rename from femtovg/Cargo.toml rename to flfemtovg/Cargo.toml index 834af3e..7cdc483 100644 --- a/femtovg/Cargo.toml +++ b/flfemtovg/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "femtovg" +name = "flfemtovg" version = "0.1.0" authors = ["Mohammed Alyousef "] edition = "2021" @@ -7,5 +7,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = { version = "1", features = ["enable-glwindow"] } +fltk = { version = "^1.4", features = ["enable-glwindow"] } femtovg = "0.1.1" diff --git a/femtovg/README.md b/flfemtovg/README.md similarity index 100% rename from femtovg/README.md rename to flfemtovg/README.md diff --git a/femtovg/ex.png b/flfemtovg/ex.png similarity index 100% rename from femtovg/ex.png rename to flfemtovg/ex.png diff --git a/femtovg/femtovg.gif b/flfemtovg/femtovg.gif similarity index 100% rename from femtovg/femtovg.gif rename to flfemtovg/femtovg.gif diff --git a/femtovg/src/main.rs b/flfemtovg/src/main.rs similarity index 100% rename from femtovg/src/main.rs rename to flfemtovg/src/main.rs diff --git a/glium/Cargo.toml b/flglium/Cargo.toml similarity index 79% rename from glium/Cargo.toml rename to flglium/Cargo.toml index a68dde2..0264e49 100644 --- a/glium/Cargo.toml +++ b/flglium/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "glium" +name = "flglium" version = "0.1.0" authors = ["Mohammed Alyousef "] edition = "2021" @@ -8,4 +8,4 @@ edition = "2021" [dependencies] glium = { version = "0.29", default-features = false } # no need for glutin -fltk = { version = "1", features = ["enable-glwindow"] } \ No newline at end of file +fltk = { version = "^1.4", features = ["enable-glwindow"] } diff --git a/glium/README.md b/flglium/README.md similarity index 100% rename from glium/README.md rename to flglium/README.md diff --git a/glium/ex.jpg b/flglium/ex.jpg similarity index 100% rename from glium/ex.jpg rename to flglium/ex.jpg diff --git a/glium/glium.gif b/flglium/glium.gif similarity index 100% rename from glium/glium.gif rename to flglium/glium.gif diff --git a/glium/src/main.rs b/flglium/src/main.rs similarity index 100% rename from glium/src/main.rs rename to flglium/src/main.rs diff --git a/glow/Cargo.toml b/flglow/Cargo.toml similarity index 69% rename from glow/Cargo.toml rename to flglow/Cargo.toml index 975092e..959104d 100644 --- a/glow/Cargo.toml +++ b/flglow/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "glow" +name = "flglow" version = "0.1.0" authors = ["MoAlyousef "] edition = "2021" @@ -7,5 +7,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = { version = "1", features = ["enable-glwindow"] } -glow = "0.7" +fltk = { version = "^1.4", features = ["enable-glwindow"] } +glow = "^0.13" diff --git a/glow/README.md b/flglow/README.md similarity index 100% rename from glow/README.md rename to flglow/README.md diff --git a/glow/ex.jpg b/flglow/ex.jpg similarity index 100% rename from glow/ex.jpg rename to flglow/ex.jpg diff --git a/glow/glow.gif b/flglow/glow.gif similarity index 100% rename from glow/glow.gif rename to flglow/glow.gif diff --git a/glow/src/main.rs b/flglow/src/main.rs similarity index 100% rename from glow/src/main.rs rename to flglow/src/main.rs diff --git a/image/Cargo.toml b/flimage/Cargo.toml similarity index 88% rename from image/Cargo.toml rename to flimage/Cargo.toml index 96257d0..d42e66d 100644 --- a/image/Cargo.toml +++ b/flimage/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "image" +name = "flimage" version = "0.1.0" authors = ["MoAlyousef "] edition = "2021" @@ -7,6 +7,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1" +fltk = "^1.4" image = "0.24" rust-embed = "6.6.1" diff --git a/image/README.md b/flimage/README.md similarity index 100% rename from image/README.md rename to flimage/README.md diff --git a/image/fltk.png b/flimage/fltk.png similarity index 100% rename from image/fltk.png rename to flimage/fltk.png diff --git a/image/src/main.rs b/flimage/src/main.rs similarity index 100% rename from image/src/main.rs rename to flimage/src/main.rs diff --git a/libmpv/Cargo.toml b/fllibmpv/Cargo.toml similarity index 76% rename from libmpv/Cargo.toml rename to fllibmpv/Cargo.toml index ff53c8f..6e23437 100644 --- a/libmpv/Cargo.toml +++ b/fllibmpv/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "libmpv" +name = "fllibmpv" version = "0.1.0" authors = ["Mohammed Alyousef "] edition = "2021" @@ -7,5 +7,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = {version = "1.1", features=["enable-glwindow", "no-pango"]} +fltk = {version = "^1.4", features=["enable-glwindow", "no-pango"]} libmpv = { git = "https://github.com/anlumo/libmpv-rs" } diff --git a/libmpv/README.md b/fllibmpv/README.md similarity index 100% rename from libmpv/README.md rename to fllibmpv/README.md diff --git a/libmpv/src/main.rs b/fllibmpv/src/main.rs similarity index 98% rename from libmpv/src/main.rs rename to fllibmpv/src/main.rs index 3a8a5f4..986b592 100644 --- a/libmpv/src/main.rs +++ b/fllibmpv/src/main.rs @@ -60,7 +60,7 @@ fn main() { w.swap_buffers(); }); - app::add_idle(move || { + app::add_idle3(move |_| { mpv_win.redraw(); }); diff --git a/libmpv/src/sys_main.rs b/fllibmpv/src/sys_main.rs similarity index 100% rename from libmpv/src/sys_main.rs rename to fllibmpv/src/sys_main.rs diff --git a/pixels/Cargo.toml b/flpixels/Cargo.toml similarity index 70% rename from pixels/Cargo.toml rename to flpixels/Cargo.toml index 93aa68b..3b47564 100644 --- a/pixels/Cargo.toml +++ b/flpixels/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "pixels" +name = "flpixels" version = "0.1.0" authors = ["Mohammed Alyousef "] edition = "2021" @@ -8,5 +8,5 @@ resolver = "2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = { version = "^1", features = ["raw-window-handle"] } -pixels = "0.9.0" +fltk = { version = "^1.4", features = ["raw-window-handle"] } +pixels = "^0.13" diff --git a/pixels/README.md b/flpixels/README.md similarity index 100% rename from pixels/README.md rename to flpixels/README.md diff --git a/pixels/build.rs b/flpixels/build.rs similarity index 100% rename from pixels/build.rs rename to flpixels/build.rs diff --git a/pixels/ex.jpg b/flpixels/ex.jpg similarity index 100% rename from pixels/ex.jpg rename to flpixels/ex.jpg diff --git a/pixels/pixels.gif b/flpixels/pixels.gif similarity index 100% rename from pixels/pixels.gif rename to flpixels/pixels.gif diff --git a/pixels/src/main.rs b/flpixels/src/main.rs similarity index 100% rename from pixels/src/main.rs rename to flpixels/src/main.rs diff --git a/plotters/Cargo.toml b/flplotters/Cargo.toml similarity index 88% rename from plotters/Cargo.toml rename to flplotters/Cargo.toml index be98153..b8934c8 100644 --- a/plotters/Cargo.toml +++ b/flplotters/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "plotters" +name = "flplotters" version = "0.1.0" authors = ["Mohammed Alyousef "] edition = "2021" @@ -7,6 +7,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1" +fltk = "^1.4" plotters = "0.3.0" plotters-bitmap = "0.3.0" diff --git a/plotters/README.md b/flplotters/README.md similarity index 100% rename from plotters/README.md rename to flplotters/README.md diff --git a/plotters/ex.jpg b/flplotters/ex.jpg similarity index 100% rename from plotters/ex.jpg rename to flplotters/ex.jpg diff --git a/plotters/plotters.gif b/flplotters/plotters.gif similarity index 100% rename from plotters/plotters.gif rename to flplotters/plotters.gif diff --git a/plotters/src/main.rs b/flplotters/src/main.rs similarity index 100% rename from plotters/src/main.rs rename to flplotters/src/main.rs diff --git a/raqote/Cargo.toml b/flraqote/Cargo.toml similarity index 82% rename from raqote/Cargo.toml rename to flraqote/Cargo.toml index 350c83c..2b9981f 100644 --- a/raqote/Cargo.toml +++ b/flraqote/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "raqote" +name = "flraqote" version = "0.1.0" authors = ["Mohammed Alyousef "] edition = "2021" @@ -7,5 +7,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1" -raqote = "0.8" \ No newline at end of file +fltk = "^1.4" +raqote = "0.8" diff --git a/raqote/README.md b/flraqote/README.md similarity index 100% rename from raqote/README.md rename to flraqote/README.md diff --git a/raqote/ex.jpg b/flraqote/ex.jpg similarity index 100% rename from raqote/ex.jpg rename to flraqote/ex.jpg diff --git a/raqote/raqote.gif b/flraqote/raqote.gif similarity index 100% rename from raqote/raqote.gif rename to flraqote/raqote.gif diff --git a/raqote/src/main.rs b/flraqote/src/main.rs similarity index 97% rename from raqote/src/main.rs rename to flraqote/src/main.rs index 89d5d95..17e406f 100644 --- a/raqote/src/main.rs +++ b/flraqote/src/main.rs @@ -1,4 +1,3 @@ -use fltk::prelude::WindowExt; use fltk::{ app, draw, enums, frame, prelude::{GroupExt, WidgetBase, WidgetExt}, @@ -92,6 +91,5 @@ pub fn draw_line(x: i32, y: i32, x2: i32, y2: i32) -> Path { let mut pb = PathBuilder::new(); pb.move_to(x as f32, y as f32); pb.line_to(x2 as f32, y2 as f32); - let path = pb.finish(); - path + pb.finish() } diff --git a/speedy2d/Cargo.toml b/flspeedy2d/Cargo.toml similarity index 83% rename from speedy2d/Cargo.toml rename to flspeedy2d/Cargo.toml index d41e6b1..5e288aa 100644 --- a/speedy2d/Cargo.toml +++ b/flspeedy2d/Cargo.toml @@ -7,6 +7,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = { version = "1", features = ["enable-glwindow"] } +fltk = { version = "^1.4", features = ["enable-glwindow"] } speedy2d = { version = "1.0.2", default-features = false } gl = "0.14" diff --git a/speedy2d/README.md b/flspeedy2d/README.md similarity index 100% rename from speedy2d/README.md rename to flspeedy2d/README.md diff --git a/speedy2d/ex.jpg b/flspeedy2d/ex.jpg similarity index 100% rename from speedy2d/ex.jpg rename to flspeedy2d/ex.jpg diff --git a/speedy2d/speedy2d.gif b/flspeedy2d/speedy2d.gif similarity index 100% rename from speedy2d/speedy2d.gif rename to flspeedy2d/speedy2d.gif diff --git a/speedy2d/src/main.rs b/flspeedy2d/src/main.rs similarity index 100% rename from speedy2d/src/main.rs rename to flspeedy2d/src/main.rs diff --git a/fltext/Cargo.toml b/fltext/Cargo.toml new file mode 100644 index 0000000..05a1613 --- /dev/null +++ b/fltext/Cargo.toml @@ -0,0 +1,33 @@ +[package] +name = "fltext" +version = "0.1.0" +edition = "2021" + +[dependencies] +fltk = "^1.4" +fltk-theme = "0.7" +regex = "1.9.6" +notify = "5.1" +# optional deps +# term +fltk-term = { version = "0.1", optional = true } +# highlight +tree-sitter-highlight = { version = "0.20", optional = true } +tree-sitter-rust = { version = "0.20", optional = true } +tree-sitter-toml = { version = "0.20", optional = true } +tree-sitter-md = { version = "0.1", optional = true } + +[features] +default = ["highlight", "term"] +highlight = [ + "tree-sitter-highlight", + "tree-sitter-rust", + "tree-sitter-toml", + "tree-sitter-md", + ] +term = [ + "fltk-term" +] + +[patch.crates-io] +fltk = { git = "https://github.com/fltk-rs/fltk-rs" } diff --git a/fltext/LICENSE b/fltext/LICENSE new file mode 100644 index 0000000..93a4574 --- /dev/null +++ b/fltext/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 Mohammed Alyousef + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/fltext/README.md b/fltext/README.md new file mode 100644 index 0000000..1bcf9b9 --- /dev/null +++ b/fltext/README.md @@ -0,0 +1,34 @@ +# red + +red or the RustyEditor is a lightweight and minimal text editor which supports multiple-tabs (ร  la vscode) and which integrates a file browser and a terminal. The editor component supports syntax highlighting via [tree-sitter-highlight](https://github.com/tree-sitter/tree-sitter/tree/master/highlight). + +## Building +```bash +git clone https://github.com/MoAlyousef/red +cd red +cargo build --release +``` + +To disable building with tree-sitter and the terminal: +```bash +cargo build --no-default-features --release +``` + +To build with native wayland support on Linux: +```bash +cargo build --features=fltk/use-wayland --release +``` + +![image](https://github.com/MoAlyousef/red/assets/37966791/c43a180f-d1db-4528-ace6-d3713dcda202) + +## Known issues +- If you're running KDE and no icons appear in the FileBrowser, you can try setting the KDEDIR to /usr/local. +- Highlighting via tree-sitter seems to vary between different language modules. tree-sitter-json seems quite limited for example. + +## ToDo +- Add a settings dialog. +- Save and get user settings using fltk [Preferences](https://docs.rs/fltk/latest/fltk/app/prefs/struct.Preferences.html). +- Enable using FLTK's FileChooser instead of the system provided one via the settings. +- Add more options to FileBrowser popup menu and the terminal menu. +- Support user provided color schemes for the app and the highlighting. +- Support regex for search & replace. diff --git a/fltext/src/cbs.rs b/fltext/src/cbs.rs new file mode 100644 index 0000000..1dec330 --- /dev/null +++ b/fltext/src/cbs.rs @@ -0,0 +1,222 @@ +use crate::state::STATE; +use fltk::{enums::*, prelude::*, *}; +use std::{fs, path::PathBuf}; + +fn nfc_get_file(mode: dialog::NativeFileChooserType) -> PathBuf { + let mut nfc = dialog::NativeFileChooser::new(mode); + nfc.show(); + nfc.filename() +} + +fn find() { + let mut dlg: window::Window = app::widget_from_id("find").unwrap(); + let main_win = app::first_window().unwrap(); + dlg.resize(main_win.x() + main_win.w() - 300, dlg.y() + 30, 300, 50); + dlg.show(); +} + +fn replace() { + let mut dlg: window::Window = app::widget_from_id("replace").unwrap(); + let main_win = app::first_window().unwrap(); + dlg.resize(main_win.x() + main_win.w() - 300, dlg.y() + 30, 300, 80); + dlg.show(); +} + +pub fn win_cb(_: &mut window::Window) { + if app::event() == Event::Close { + app::quit(); + } +} + +pub fn editor_cb(_e: &mut text::TextEditor) { + app::add_timeout3(0.01, |_| STATE.with(|s| s.was_modified(true))); +} + +pub fn new_file() { + let dlg = dialog::input_default("Enter file name", ""); + if let Some(f) = dlg { + fs::File::create(f).ok(); + } +} + +pub fn new_dir() { + let dlg = dialog::input_default("Enter directory name", ""); + if let Some(f) = dlg { + fs::create_dir(f).ok(); + } +} + +pub fn menu_cb(m: &mut impl MenuExt) { + if let Ok(mpath) = m.item_pathname(None) { + match mpath.as_str() { + "&File/New File...\t" => { + new_file(); + } + "&File/New Dir...\t" => { + new_dir(); + } + "&File/Open...\t" => { + let c = nfc_get_file(dialog::NativeFileChooserType::BrowseFile); + if c.exists() { + STATE.with(move |s| { + s.append(Some(c.canonicalize().unwrap())); + }); + } + } + "&File/Save\t" => { + STATE.with(|s| { + if let Some(id) = s.current_id() { + let e = s.map.get(&id).unwrap(); + let modified = e.modified; + if let Some(current_file) = e.current_file.as_ref() { + if modified && current_file.exists() { + fs::write(current_file, e.buf.text()).ok(); + s.was_modified(false); + } + } + } + }); + } + "&File/Save as...\t" => { + let c = nfc_get_file(dialog::NativeFileChooserType::BrowseSaveFile); + if c.exists() { + STATE.with(move |s| { + if let Some(buf) = s.buf().as_ref() { + fs::write(&c, buf.text()).expect("Failed to write to file!"); + s.was_modified(false); + } + }); + } + } + "&File/Save All\t" => { + STATE.with(|s| { + for v in s.map.values_mut() { + if v.modified && v.current_file.as_ref().unwrap().exists() { + fs::write(v.current_file.as_ref().unwrap(), v.buf.text()).ok(); + v.modified = true; + } + } + }); + } + "&File/Quit\t" => app::quit(), + "/Undo\t" | "&Edit/Undo\t" => STATE.with(|s| { + if let Some(e) = s.current_editor() { + e.undo() + } + }), + "/Redo\t" | "&Edit/Redo\t" => STATE.with(|s| { + if let Some(e) = s.current_editor() { + e.redo() + } + }), + "/Cut\t" | "&Edit/Cut\t" => STATE.with(|s| { + if let Some(e) = s.current_editor() { + e.cut() + } + }), + "/Copy\t" | "&Edit/Copy\t" => STATE.with(|s| { + if let Some(e) = s.current_editor() { + e.copy() + } + }), + "/Paste\t" | "&Edit/Paste\t" => STATE.with(|s| { + if let Some(e) = s.current_editor() { + e.paste() + } + }), + "/Find\t" | "&Edit/Find\t" => find(), + "/Replace\t" | "&Edit/Replace\t" => replace(), + "&View/File browser\t" => { + let mut item = m.at(m.value()).unwrap(); + let fbr: group::Group = app::widget_from_id("fbr_group").unwrap(); + let mut parent = group::Flex::from_dyn_widget(&fbr.parent().unwrap()).unwrap(); + if !item.value() { + parent.fixed(&fbr, 1); + item.clear(); + } else { + parent.fixed(&fbr, 180); + item.set(); + } + app::redraw(); + } + "&View/Terminal\t" => { + let mut item = m.at(m.value()).unwrap(); + let term: group::Group = app::widget_from_id("term_group").unwrap(); + let mut parent = group::Flex::from_dyn_widget(&term.parent().unwrap()).unwrap(); + if !item.value() { + parent.fixed(&term, 1); + item.clear(); + } else { + parent.fixed(&term, 160); + item.set(); + } + app::redraw(); + } + "&Help/About\t" => { + dialog::message_title("About"); + dialog::message_default("A minimal text editor written using fltk-rs!") + } + _ => unreachable!(), + } + } +} + +pub fn tab_close_cb(g: &mut impl GroupExt) { + if app::callback_reason() == CallbackReason::Closed { + let ed = text::TextEditor::from_dyn_widget(&g.child(0).unwrap()).unwrap(); + let edid = ed.as_widget_ptr() as usize; + let buf = ed.buffer().unwrap(); + let mut parent = g.parent().unwrap(); + parent.remove(g); + unsafe { + text::TextBuffer::delete(buf); + } + STATE.with(move |s| s.map.remove(&edid)); + parent.set_damage(true); + } +} + +#[cfg(feature = "term")] +pub fn tab_splitter_cb(f: &mut frame::Frame, ev: Event) -> bool { + let mut parent = group::Flex::from_dyn_widget(&f.parent().unwrap()).unwrap(); + let term = app::widget_from_id::("term_group").unwrap(); + match ev { + Event::Push => true, + Event::Drag => { + parent.fixed(&term, parent.h() + parent.y() - app::event_y()); + app::redraw(); + true + } + Event::Enter => { + f.window().unwrap().set_cursor(Cursor::NS); + true + } + Event::Leave => { + f.window().unwrap().set_cursor(Cursor::Arrow); + true + } + _ => false, + } +} + +pub fn fbr_splitter_cb(f: &mut frame::Frame, ev: Event) -> bool { + let mut parent = group::Flex::from_dyn_widget(&f.parent().unwrap()).unwrap(); + let fbr: group::Group = app::widget_from_id("fbr_group").unwrap(); + match ev { + Event::Push => true, + Event::Drag => { + parent.fixed(&fbr, app::event_x()); + app::redraw(); + true + } + Event::Enter => { + f.window().unwrap().set_cursor(Cursor::WE); + true + } + Event::Leave => { + f.window().unwrap().set_cursor(Cursor::Arrow); + true + } + _ => false, + } +} diff --git a/fltext/src/dialogs.rs b/fltext/src/dialogs.rs new file mode 100644 index 0000000..7608bf1 --- /dev/null +++ b/fltext/src/dialogs.rs @@ -0,0 +1,199 @@ +#![allow(dead_code)] + +use crate::state::STATE; +use fltk::{prelude::*, *}; +use std::cell::RefCell; +use std::rc::Rc; + +pub struct FindDialog { + win: window::Window, +} + +impl FindDialog { + pub fn new() -> Self { + let idx = Rc::from(RefCell::from(0)); + let mut win = window::Window::new(0, 0, 300, 50, "Find").with_id("find"); + win.set_border(false); + let mut row = group::Flex::default_fill(); + row.set_margin(10); + let f = frame::Frame::default().with_label("Find:"); + row.fixed(&f, 30); + let mut i = input::Input::default(); + i.set_trigger(enums::CallbackTrigger::EnterKeyAlways); + let mut reg = button::ToggleButton::default().with_label(".*"); + reg.set_selection_color(reg.color().lighter()); + reg.set_tooltip("Use regex"); + row.fixed(®, 30); + let mut b = button::Button::default().with_label("Next"); + i.set_callback({ + move |i| { + let val = i.value(); + let reg_val = reg.value(); + if reg_val && regex::Regex::new(&val).is_err() { + i.set_text_color(enums::Color::Red); + return; + } else { + i.set_text_color(enums::Color::Foreground); + } + if !val.is_empty() { + STATE.with({ + let idx = idx.clone(); + move |s| { + if let Some(buf) = s.buf().as_mut() { + let text = buf.text(); + if reg_val { + if let Ok(re) = regex::Regex::new(&val) { + let v: Vec<_> = + re.find_iter(&text).map(|m| m.range()).collect(); + if !v.is_empty() { + let mut idx = idx.borrow_mut(); + let curr = &v[*idx]; + let mut ed: text::TextEditor = + s.current_editor().unwrap(); + buf.select(curr.start as i32, curr.end as i32); + ed.scroll( + ed.count_lines(0, curr.start as i32, true), + 0, + ); + *idx += 1; + if *idx == v.len() { + *idx = 0; + } + } + } + } else { + let v: Vec<_> = text.match_indices(&val).collect(); + if !v.is_empty() { + let mut idx = idx.borrow_mut(); + let curr = v[*idx]; + let mut ed: text::TextEditor = s.current_editor().unwrap(); + buf.select(curr.0 as i32, (curr.0 + val.len()) as i32); + ed.scroll(ed.count_lines(0, curr.0 as i32, true), 0); + *idx += 1; + if *idx == v.len() { + *idx = 0; + } + } + } + } + } + }); + } + } + }); + b.set_callback(move |_| i.do_callback()); + row.fixed(&b, 60); + row.end(); + win.end(); + win.handle(|win, ev| match ev { + enums::Event::Hide => { + win.hide(); + true + } + enums::Event::Close => { + win.hide(); + true + } + _ => false, + }); + Self { win } + } +} + +pub struct ReplaceDialog { + win: window::Window, +} + +impl ReplaceDialog { + pub fn new() -> Self { + let mut win = window::Window::new(0, 0, 300, 80, "Replace").with_id("replace"); + win.set_border(false); + let mut col = group::Flex::default_fill().column(); + col.set_margin(5); + let mut row = group::Flex::default(); + let f = frame::Frame::default().with_label("Search:"); + row.fixed(&f, 60); + let mut search = input::Input::default(); + search.set_trigger(enums::CallbackTrigger::Changed); + let mut reg = button::ToggleButton::default().with_label(".*"); + reg.set_selection_color(reg.color().lighter()); + reg.set_tooltip("Use regex"); + row.fixed(®, 30); + row.end(); + let mut row = group::Flex::default(); + let f = frame::Frame::default().with_label("Replace:"); + row.fixed(&f, 60); + let replace = input::Input::default(); + let mut b = button::Button::default().with_label("@>"); + b.set_tooltip("Apply"); + row.fixed(&b, 30); + row.end(); + col.end(); + win.end(); + search.set_callback({ + let reg = reg.clone(); + move |i| { + let val = i.value(); + let reg_val = reg.value(); + if reg_val && regex::Regex::new(&val).is_err() { + i.set_text_color(enums::Color::Red); + } else { + i.set_text_color(enums::Color::Foreground); + } + } + }); + b.set_callback(move |_| { + let search = search.value(); + let replace = replace.value(); + let reg_val = reg.value(); + if reg_val && regex::Regex::new(&search).is_err() { + return; + } + STATE.with({ + move |s| { + if let Some(buf) = s.buf().as_mut() { + let text = buf.text(); + if reg_val { + if let Ok(re) = regex::Regex::new(&search) { + let ntext = re.replace(&text, &replace); + buf.set_text(&ntext); + } + } else { + let ntext = text.replace(&search, &replace); + buf.set_text(&ntext); + } + s.was_modified(true); + } + } + }); + }); + win.handle(|win, ev| match ev { + enums::Event::Hide => { + win.hide(); + true + } + enums::Event::Close => { + win.hide(); + true + } + _ => false, + }); + Self { win } + } +} + +pub struct ImageDialog { + win: window::Window, +} + +impl ImageDialog { + pub fn new() -> Self { + let mut win = window::Window::default() + .with_size(400, 300) + .with_id("image_dialog"); + let mut f = frame::Frame::default_fill(); + win.end(); + win.resize_callback(move |_win, _, _, w, h| f.resize(0, 0, w, h)); + Self { win } + } +} diff --git a/fltext/src/fbr.rs b/fltext/src/fbr.rs new file mode 100644 index 0000000..34e49f1 --- /dev/null +++ b/fltext/src/fbr.rs @@ -0,0 +1,128 @@ +#![allow(clippy::single_match)] + +use crate::{cbs, state::STATE, utils}; +use fltk::{enums::*, prelude::*, *}; +use notify::{event::EventKind, Event, RecursiveMode, Watcher}; +use std::{ + env, + path::{Path, PathBuf}, +}; + +pub fn menu_cb(m: &mut impl MenuExt) { + if let Ok(mpath) = m.item_pathname(None) { + match mpath.as_str() { + "/New File...\t" => cbs::new_file(), + "/New Dir...\t" => cbs::new_dir(), + _ => (), + } + } +} + +pub fn init_menu(m: &mut (impl MenuExt + 'static)) { + m.add( + "New File...\t", + Shortcut::Ctrl | 'n', + menu::MenuFlag::Normal, + menu_cb, + ); + m.add( + "New Dir...\t", + Shortcut::Ctrl | Shortcut::Shift | 'n', + menu::MenuFlag::Normal, + menu_cb, + ); +} + +pub fn fbr_cb(f: &mut browser::FileBrowser, watcher: &mut dyn Watcher) { + if let Some(path) = f.text(f.value()) { + let path = PathBuf::from(path); + if path.exists() { + if path.is_dir() { + watcher.watch(&path, RecursiveMode::NonRecursive).unwrap(); + f.load(&path).expect("Couldn't load directory!"); + let cwd = env::current_dir().unwrap(); + env::set_current_dir(cwd.join(path)).unwrap(); + let mut info: frame::Frame = app::widget_from_id("info").unwrap(); + info.set_label(&format!( + "Directory: {}", + utils::strip_unc_path(&env::current_dir().unwrap()) + )); + f.set_damage(true); + } else { + let mut is_image = false; + if let Some(ext) = path.extension() { + match ext.to_str().unwrap() { + "jpg" | "gif" | "png" | "bmp" => is_image = true, + _ => (), + } + } + if is_image { + let img = image::SharedImage::load(path).unwrap(); + let mut win: window::Window = app::widget_from_id("image_dialog").unwrap(); + win.resize(win.x(), win.y(), img.w(), img.h()); + win.child(0).unwrap().set_image(Some(img)); + win.show(); + } else { + STATE.with(move |s| { + s.append(Some(path.canonicalize().unwrap())); + }); + } + } + } + } +} + +pub struct Fbr { + g: group::Group, +} + +impl Fbr { + pub fn new(current_path: &Path) -> Self { + let mut g = group::Group::default().with_id("fbr_group"); + let mut fbr = browser::FileBrowser::default() + .with_type(browser::BrowserType::Hold) + .with_id("fbr"); + fbr.load(current_path) + .expect("Failed to load working directory"); + fbr.set_color(Color::Background.darker()); + let mut m = menu::MenuButton::default() + .with_type(menu::MenuButtonType::Popup3) + .with_id("pop1"); + init_menu(&mut m); + g.end(); + let mut watcher = notify::recommended_watcher({ + let mut fbr = fbr.clone(); + move |res: Result| match res { + Ok(event) => { + let mut needs_update = false; + match event.kind { + EventKind::Create(_) => { + needs_update = true; + } + EventKind::Remove(_) => { + needs_update = true; + } + _ => (), + } + if needs_update { + fbr.load(env::current_dir().unwrap()).unwrap(); + } + } + Err(e) => eprintln!("{}", e), + } + }) + .unwrap(); + watcher + .watch(current_path, RecursiveMode::NonRecursive) + .unwrap(); + fbr.set_callback(move |f| fbr_cb(f, &mut watcher)); + g.resize_callback(move |_, x, y, w, h| { + m.resize(x, y, w, h); + fbr.resize(x, y, w, h); + }); + + Self { g } + } +} + +fltk::widget_extends!(Fbr, group::Group, g); diff --git a/fltext/src/gui.rs b/fltext/src/gui.rs new file mode 100644 index 0000000..9c09820 --- /dev/null +++ b/fltext/src/gui.rs @@ -0,0 +1,269 @@ +use crate::{cbs, dialogs, fbr, utils}; +use fltk::{enums::*, prelude::*, *}; +use fltk_theme::{color_themes, ColorTheme}; +use std::path::{Path, PathBuf}; + +#[cfg(feature = "term")] +use fltk_term as term; + +#[cfg(feature = "highlight")] +use crate::highlight; + +const WIDTH: i32 = 800; +const HEIGHT: i32 = 600; +const MENU_HEIGHT: i32 = if cfg!(target_os = "macos") { 1 } else { 30 }; + +pub fn init_gui(current_file: &Option, current_path: &Path) -> app::App { + ColorTheme::new(color_themes::DARK_THEME).apply(); + app::set_scheme(app::Scheme::Plastic); + app::set_font(Font::Courier); + app::set_menu_linespacing(10); + let mut buf = text::TextBuffer::default(); + buf.set_tab_distance(4); + + let _find_dialog = dialogs::FindDialog::new(); + let _replace_dialog = dialogs::ReplaceDialog::new(); + let _image_dialog = dialogs::ImageDialog::new(); + + let mut popup = menu::MenuButton::default().with_type(menu::MenuButtonType::Popup3); + init_edit_menu(&mut popup, ""); + + let mut w = window::Window::default() + .with_size(WIDTH, HEIGHT) + .with_label("flText"); + w.set_xclass("red"); + + let mut col0 = group::Flex::default_fill().column(); + col0.set_pad(2); + let mut m = menu::SysMenuBar::default().with_id("menu"); + init_menu(&mut m, current_file.is_none()); + col0.fixed(&m, MENU_HEIGHT); + let mut row = group::Flex::default(); + row.set_pad(0); + let fbr = fbr::Fbr::new(current_path); + if current_file.is_none() { + row.fixed(&*fbr, 180); + } else { + row.fixed(&*fbr, 1); + } + let mut fbr_splitter = frame::Frame::default(); + fbr_splitter.handle(cbs::fbr_splitter_cb); + row.fixed(&fbr_splitter, 4); + let mut col = group::Flex::default().column(); + col.set_pad(0); + let mut tabs = group::Tabs::default().with_id("tabs"); + tabs.handle(move |t, ev| tabs_handle(t, ev, &mut popup)); + tabs.handle_overflow(group::TabsOverflow::Pulldown); + tabs.end(); + tabs.auto_layout(); + #[cfg(feature = "term")] + { + let mut tab_splitter = frame::Frame::default(); + tab_splitter.handle(cbs::tab_splitter_cb); + col.fixed(&tab_splitter, 4); + let term = term::PPTerm::default(); + col.fixed(&*term, 160); + } + col.end(); + row.end(); + let info = frame::Frame::default() + .with_label(&format!( + "Directory: {}", + utils::strip_unc_path(current_path) + )) + .with_align(enums::Align::Left | enums::Align::Inside) + .with_id("info"); + col0.fixed(&info, 20); + col0.end(); + w.resizable(&row); + w.end(); + w.make_resizable(true); + w.show(); + w.set_callback(cbs::win_cb); + app::App::default() +} + +pub fn tabs_handle(t: &mut group::Tabs, ev: Event, popup: &mut menu::MenuButton) -> bool { + match ev { + Event::Push => { + if app::event_mouse_button() == app::MouseButton::Right + && app::event_y() > t.y() + 30 + && t.children() > 0 + { + popup.popup(); + true + } else { + false + } + } + _ => false, + } +} + +pub fn init_edit_menu(m: &mut (impl MenuExt + 'static), header: &str) { + m.add( + &format!("{}Undo\t", header), + Shortcut::Ctrl | 'z', + menu::MenuFlag::Normal, + cbs::menu_cb, + ); + m.add( + &format!("{}Redo\t", header), + Shortcut::Ctrl | 'y', + menu::MenuFlag::MenuDivider, + cbs::menu_cb, + ); + m.add( + &format!("{}Cut\t", header), + Shortcut::Ctrl | 'x', + menu::MenuFlag::Normal, + cbs::menu_cb, + ); + m.add( + &format!("{}Copy\t", header), + Shortcut::Ctrl | 'c', + menu::MenuFlag::Normal, + cbs::menu_cb, + ); + m.add( + &format!("{}Paste\t", header), + Shortcut::Ctrl | 'v', + menu::MenuFlag::MenuDivider, + cbs::menu_cb, + ); + m.add( + &format!("{}Find\t", header), + Shortcut::Ctrl | 'f', + menu::MenuFlag::Normal, + cbs::menu_cb, + ); + m.add( + &format!("{}Replace\t", header), + Shortcut::Ctrl | 'h', + menu::MenuFlag::Normal, + cbs::menu_cb, + ); +} +pub fn init_menu(m: &mut (impl MenuExt + 'static), load_dir: bool) { + m.add( + "&File/New File...\t", + Shortcut::Ctrl | 'n', + menu::MenuFlag::Normal, + cbs::menu_cb, + ); + m.add( + "&File/New Dir...\t", + Shortcut::Ctrl | Shortcut::Shift | 'n', + menu::MenuFlag::Normal, + cbs::menu_cb, + ); + m.add( + "&File/Open...\t", + Shortcut::Ctrl | 'o', + menu::MenuFlag::Normal, + cbs::menu_cb, + ); + m.add( + "&File/Save\t", + Shortcut::Ctrl | 's', + menu::MenuFlag::Normal, + cbs::menu_cb, + ); + m.add( + "&File/Save as...\t", + Shortcut::Ctrl | Shortcut::Shift | 'w', + menu::MenuFlag::Normal, + cbs::menu_cb, + ); + m.add( + "&File/Save All\t", + Shortcut::None, + menu::MenuFlag::MenuDivider, + cbs::menu_cb, + ); + let idx = m.add( + "&File/Quit\t", + Shortcut::Ctrl | 'q', + menu::MenuFlag::Normal, + cbs::menu_cb, + ); + m.at(idx) + .unwrap() + .set_label_color(Color::from_hex(0xdc322f)); + init_edit_menu(m, "&Edit/"); + let idx = m.add( + "&View/File browser\t", + Shortcut::None, + menu::MenuFlag::Toggle, + cbs::menu_cb, + ); + if load_dir { + m.at(idx).unwrap().set(); + } + #[cfg(feature = "term")] + { + let idx = m.add( + "&View/Terminal\t", + Shortcut::None, + menu::MenuFlag::Toggle, + cbs::menu_cb, + ); + m.at(idx).unwrap().set(); + } + m.add( + "&Help/About\t", + Shortcut::None, + menu::MenuFlag::Normal, + cbs::menu_cb, + ); +} + +pub fn build_editor(id: &str) -> text::TextEditor { + let mut texteditor = text::TextEditor::default().with_id(id); + texteditor.set_color(Color::from_hex(0x002b36)); + texteditor.set_linenumber_width(40); + texteditor.set_linenumber_size(12); + texteditor.set_linenumber_fgcolor(Color::from_hex(0xb58900)); + texteditor.set_linenumber_bgcolor(Color::Background.darker()); + texteditor.set_text_font(Font::Courier); + texteditor.set_trigger(CallbackTrigger::Changed); + texteditor.set_callback(cbs::editor_cb); + texteditor +} + +pub fn create_ed( + tabs: &mut group::Tabs, + id: &str, + current_path: &Option, +) -> text::TextEditor { + tabs.begin(); + let mut edrow = group::Flex::default() + .row() + .with_label(if let Some(current_path) = current_path.as_ref() { + if current_path.is_dir() { + "untitled" + } else { + current_path.file_name().unwrap().to_str().unwrap() + } + } else { + "untitled" + }) + .with_id(id); + edrow.set_trigger(CallbackTrigger::Closed); + edrow.set_callback(cbs::tab_close_cb); + let mut ed = build_editor("ed"); + edrow.end(); + tabs.end(); + tabs.auto_layout(); + tabs.set_value(&edrow).ok(); + + let mut buf = text::TextBuffer::default(); + buf.set_tab_distance(4); + if let Some(p) = current_path.as_ref() { + buf.load_file(p).ok(); + #[cfg(feature = "highlight")] + highlight::highlight(p, &mut ed, &mut buf); + } + ed.set_buffer(buf); + ed +} diff --git a/fltext/src/highlight/colors.rs b/fltext/src/highlight/colors.rs new file mode 100644 index 0000000..35026a2 --- /dev/null +++ b/fltext/src/highlight/colors.rs @@ -0,0 +1,9 @@ +pub const GREEN: u32 = 0x859900; +pub const RED: u32 = 0xdc322f; +pub const YELLOW: u32 = 0xb58900; +pub const DARKYELLOW: u32 = 0xc69a66; +pub const WHITE: u32 = 0xabb2bf; +pub const BLUE: u32 = 0x268bd2; +pub const PURPLE: u32 = 0xc678dd; +pub const GREY: u32 = 0x808080; +pub const LIGHTGREY: u32 = 0xd8d8d8; diff --git a/fltext/src/highlight/md.rs b/fltext/src/highlight/md.rs new file mode 100644 index 0000000..85f09e7 --- /dev/null +++ b/fltext/src/highlight/md.rs @@ -0,0 +1,23 @@ +use super::colors::*; +use super::HighlightData; +use tree_sitter_highlight::HighlightConfiguration; + +use tree_sitter_md as ts; + +pub const STYLES: &[(&str, u32)] = &[ + ("DEFAULT", WHITE), + ("text.title", RED), + ("text.reference", GREY), + ("punctuation.special", RED), + ("text.literal", GREEN), + ("punctuation.delimiter", DARKYELLOW), + ("text.uri", DARKYELLOW), +]; + +pub fn lang_data() -> HighlightData { + let (names, styles) = super::resolve_styles(STYLES); + let mut config = + HighlightConfiguration::new(ts::language(), ts::HIGHLIGHT_QUERY_BLOCK, "", "").unwrap(); + config.configure(&names); + HighlightData::new(styles, config, None) +} diff --git a/fltext/src/highlight/mod.rs b/fltext/src/highlight/mod.rs new file mode 100644 index 0000000..fca4094 --- /dev/null +++ b/fltext/src/highlight/mod.rs @@ -0,0 +1,130 @@ +use fltk::{ + app, + enums::{Color, Font}, + prelude::DisplayExt, + text::{StyleTableEntry, TextBuffer, TextEditor}, +}; +use std::path::Path; +use tree_sitter_highlight::HighlightConfiguration; +use tree_sitter_highlight::HighlightEvent; +use tree_sitter_highlight::Highlighter; + +mod colors; +mod md; +mod rust; +mod toml; + +fn translate_style(idx: usize) -> char { + char::from_u32(65 + idx as u32).unwrap() +} + +fn resolve_styles(v: &[(&'static str, u32)]) -> (Vec<&'static str>, Vec) { + let mut names = Vec::new(); + let mut styles = Vec::new(); + for elem in v { + names.push(elem.0); + styles.push(StyleTableEntry { + color: Color::from_hex(elem.1), + font: Font::Courier, + size: app::font_size(), + }); + } + (names, styles) +} + +pub struct HighlightData { + styles: Vec, + config: HighlightConfiguration, + exeption_fn: Option char>, +} + +impl HighlightData { + pub fn new( + styles: Vec, + config: HighlightConfiguration, + exeption_fn: Option char>, + ) -> Self { + Self { + styles, + config, + exeption_fn, + } + } +} + +fn get_highlight(p: &Path) -> Option { + if let Some(ext) = p.extension() { + match ext.to_str().unwrap() { + "rs" => Some(rust::lang_data()), + "toml" => Some(toml::lang_data()), + "md" => Some(md::lang_data()), + _ => None, + } + } else { + None + } +} + +pub fn highlight(p: &Path, ed: &mut TextEditor, buf: &mut TextBuffer) { + if let Some(HighlightData { + styles, + config, + exeption_fn, + }) = get_highlight(p) + { + let mut highlighter = Highlighter::new(); + let mut sbuf = TextBuffer::default(); + ed.set_highlight_data(sbuf.clone(), styles); + apply( + &mut highlighter, + &config, + &buf.text(), + &mut sbuf, + &exeption_fn, + ); + buf.add_modify_callback({ + let buf = buf.clone(); + move |_, _, _, _, _| { + apply( + &mut highlighter, + &config, + &buf.text(), + &mut sbuf, + &exeption_fn, + ); + } + }); + } +} + +fn apply( + highlighter: &mut Highlighter, + config: &HighlightConfiguration, + s: &str, + sbuf: &mut TextBuffer, + exeption_fn: &Option char>, +) { + let highlights = highlighter + .highlight(config, s.as_bytes(), None, |_| None) + .unwrap(); + + let mut local_buf = "A".repeat(s.len()); + let mut curr = 0; + for event in highlights { + match event.unwrap() { + HighlightEvent::HighlightStart(s) => { + curr = s.0; + } + HighlightEvent::Source { start, end } => { + let c = if let Some(f) = exeption_fn { + f(curr, &s[start..end]) + } else { + translate_style(curr) + }; + local_buf.replace_range(start..end, &c.to_string().repeat(end - start)); + } + HighlightEvent::HighlightEnd => curr = 0, + } + } + sbuf.set_text(&local_buf); +} diff --git a/fltext/src/highlight/rust.rs b/fltext/src/highlight/rust.rs new file mode 100644 index 0000000..4c6e487 --- /dev/null +++ b/fltext/src/highlight/rust.rs @@ -0,0 +1,36 @@ +use super::colors::*; +use super::HighlightData; +use tree_sitter_highlight::HighlightConfiguration; + +use tree_sitter_rust as ts; + +pub const STYLES: &[(&str, u32)] = &[ + ("DEFAULT", WHITE), + ("attribute", RED), + ("constructor", DARKYELLOW), + ("comment", GREY), + ("constant", DARKYELLOW), + ("constant.builtin", DARKYELLOW), + ("function", BLUE), + ("function.method", BLUE), + ("keyword", PURPLE), + ("operator", WHITE), + ("property", RED), + ("punctuation.bracket", DARKYELLOW), + ("punctuation.delimiter", WHITE), + ("string", GREEN), + ("type", YELLOW), + ("type.builtin", YELLOW), + ("variable", RED), + ("variable.builtin", RED), + ("variable.parameter", WHITE), + ("label", WHITE), +]; + +pub fn lang_data() -> HighlightData { + let (names, styles) = super::resolve_styles(STYLES); + let mut config = + HighlightConfiguration::new(ts::language(), ts::HIGHLIGHT_QUERY, "", "").unwrap(); + config.configure(&names); + HighlightData::new(styles, config, None) +} diff --git a/fltext/src/highlight/toml.rs b/fltext/src/highlight/toml.rs new file mode 100644 index 0000000..5212d9a --- /dev/null +++ b/fltext/src/highlight/toml.rs @@ -0,0 +1,24 @@ +use super::colors::*; +use super::HighlightData; +use tree_sitter_highlight::HighlightConfiguration; + +use tree_sitter_toml as ts; + +pub const STYLES: &[(&str, u32)] = &[ + ("DEFAULT", RED), + ("property", RED), + ("comment", GREY), + ("string", GREEN), + ("number", GREEN), + ("operator", LIGHTGREY), + ("punctuation", DARKYELLOW), + ("constant.builtin", DARKYELLOW), +]; + +pub fn lang_data() -> HighlightData { + let (names, styles) = super::resolve_styles(STYLES); + let mut config = + HighlightConfiguration::new(ts::language(), ts::HIGHLIGHT_QUERY, "", "").unwrap(); + config.configure(&names); + HighlightData::new(styles, config, None) +} diff --git a/fltext/src/main.rs b/fltext/src/main.rs new file mode 100644 index 0000000..7fb654f --- /dev/null +++ b/fltext/src/main.rs @@ -0,0 +1,18 @@ +use std::env; + +mod cbs; +mod dialogs; +mod fbr; +mod gui; +mod state; +mod utils; + +#[cfg(feature = "highlight")] +mod highlight; + +fn main() { + let (current_file, current_path) = utils::init_args(env::args()); + let a = gui::init_gui(¤t_file, ¤t_path); + state::init_state(current_file, current_path); + a.run().unwrap(); +} diff --git a/fltext/src/state.rs b/fltext/src/state.rs new file mode 100644 index 0000000..c9d7646 --- /dev/null +++ b/fltext/src/state.rs @@ -0,0 +1,143 @@ +#![allow(dead_code)] + +use crate::gui; +use fltk::{app, group, prelude::*, text, utils::oncelock::Lazy}; +use std::collections::HashMap; +use std::{ + path::PathBuf, + sync::atomic::{AtomicU32, Ordering}, +}; + +static COUNT: AtomicU32 = AtomicU32::new(0); + +#[derive(Clone, Debug)] +pub struct MyBuffer { + pub modified: bool, + pub id: String, + pub buf: text::TextBuffer, + pub current_file: Option, +} + +pub struct State { + pub map: HashMap, + pub current_dir: PathBuf, +} + +impl State { + pub fn new(current_dir: PathBuf) -> Self { + let map = HashMap::default(); + State { map, current_dir } + } + pub fn append(&mut self, current_path: Option) { + let mut tabs: group::Tabs = app::widget_from_id("tabs").unwrap(); + let mut open = false; + let mut edid = 0; + for (k, v) in &self.map { + if v.current_file == current_path { + open = true; + edid = *k; + break; + } + } + if !open { + let old_count = COUNT.load(Ordering::Relaxed); + let id = format!("edrow{}", old_count); + COUNT.store(old_count + 1, Ordering::Relaxed); + let ed = gui::create_ed(&mut tabs, &id, ¤t_path); + let mybuf = MyBuffer { + modified: false, + id, + buf: ed.buffer().unwrap(), + current_file: current_path.map(|p| p.canonicalize().unwrap()), + }; + self.map.insert(ed.as_widget_ptr() as usize, mybuf); + } else { + tabs.set_value( + &text::TextEditor::from_dyn_widget_ptr(edid as *mut _) + .unwrap() + .parent() + .unwrap(), + ) + .ok(); + tabs.set_damage(true); + } + } + pub fn current_id(&self) -> Option { + let tabs: group::Tabs = app::widget_from_id("tabs").unwrap(); + if tabs.children() == 0 { + return None; + } + tabs.value() + .unwrap() + .child(0) + .map(|ed| ed.as_widget_ptr() as usize) + } + pub fn was_modified(&mut self, flag: bool) { + let mut tabs: group::Tabs = app::widget_from_id("tabs").unwrap(); + if tabs.children() == 0 { + return; + } + let mut edrow = tabs.value().unwrap(); + if let Some(c) = edrow.child(0) { + let id = c.as_widget_ptr() as usize; + let mybuf = self.map.get_mut(&id).unwrap(); + mybuf.modified = flag; + if let Some(f) = mybuf.current_file.as_ref() { + if flag { + edrow.set_label(&format!("\t{} *", f.file_name().unwrap().to_str().unwrap())); + } else { + edrow.set_label(&format!("\t{}", f.file_name().unwrap().to_str().unwrap())); + } + tabs.redraw(); + } + } + } + pub fn modified(&self) -> bool { + if let Some(current_id) = self.current_id() { + let mybuf = self.map.get(¤t_id).unwrap(); + mybuf.modified + } else { + false + } + } + pub fn buf(&self) -> Option { + if let Some(current_id) = self.current_id() { + let mybuf = self.map.get(¤t_id).unwrap(); + Some(mybuf.buf.clone()) + } else { + None + } + } + pub fn current_file(&self) -> Option { + if let Some(current_id) = self.current_id() { + let mybuf = self.map.get(¤t_id).unwrap(); + mybuf.current_file.clone() + } else { + None + } + } + pub fn set_current_file(&mut self, path: PathBuf) { + if let Some(current_id) = self.current_id() { + let mybuf = self.map.get_mut(¤t_id).unwrap(); + mybuf.current_file = Some(path) + } + } + pub fn current_editor(&self) -> Option { + let tabs: group::Tabs = app::widget_from_id("tabs").unwrap(); + if tabs.children() == 0 { + return None; + } + tabs.value() + .unwrap() + .child(0) + .map(|c| text::TextEditor::from_dyn_widget(&c).unwrap()) + } +} + +pub static STATE: Lazy> = Lazy::new(app::GlobalState::::get); + +pub fn init_state(current_file: Option, current_path: PathBuf) { + let mut state = State::new(current_path); + state.append(current_file); + app::GlobalState::new(state); +} diff --git a/fltext/src/utils.rs b/fltext/src/utils.rs new file mode 100644 index 0000000..2652355 --- /dev/null +++ b/fltext/src/utils.rs @@ -0,0 +1,63 @@ +use std::{ + env, + path::{Path, PathBuf}, + process::Command, +}; + +pub fn strip_unc_path(p: &Path) -> String { + let p = p.to_str().unwrap(); + if let Some(end) = p.strip_prefix("\\\\?\\") { + end.to_string() + } else { + p.to_string() + } +} + +#[allow(dead_code)] +pub fn has_program(prog: &str) -> bool { + // hacky + match Command::new(prog).arg("--version").output() { + Ok(out) => !out.stdout.is_empty(), + _ => false, + } +} + +pub fn init_args(args: env::Args) -> (Option, PathBuf) { + let args: Vec<_> = args.collect(); + let mut current_file: Option = None; + // fix our working dir + if args.len() > 1 { + let path = PathBuf::from(args[1].clone()); + if path.exists() { + if path.is_dir() { + env::set_current_dir(path.clone()).unwrap(); + } else { + current_file = Some(PathBuf::from(path.file_name().unwrap())); + if let Some(parent) = path.parent() { + if parent.exists() { + env::set_current_dir(parent).unwrap(); + } + } + } + } + path + } else { + env::current_dir().unwrap() + }; + + let current_path = env::current_dir().unwrap().canonicalize().unwrap(); + (current_file, current_path) +} + +#[allow(dead_code)] +pub fn can_use_xterm() -> bool { + if cfg!(not(any(target_os = "macos", target_os = "windows"))) { + if let Ok(var) = env::var("XDG_SESSION_TYPE") { + var == "x11" && has_program("xterm") + } else { + env::var("RED_XTERM").is_ok() + } + } else { + false + } +} diff --git a/wgpu/Cargo.toml b/flwgpu/Cargo.toml similarity index 68% rename from wgpu/Cargo.toml rename to flwgpu/Cargo.toml index f3addca..92c9d35 100644 --- a/wgpu/Cargo.toml +++ b/flwgpu/Cargo.toml @@ -1,5 +1,5 @@ [package] -name = "wgpu" +name = "flwgpu" version = "0.1.0" authors = ["Mohammed Alyousef ", "Rongcui Dong "] edition = "2021" @@ -8,6 +8,6 @@ resolver = "2" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = { version = "^1", features = ["fltk-bundled", "raw-window-handle"] } -wgpu = "^0.13" -pollster = "^0.2" +fltk = { version = "^1.4", features = ["fltk-bundled", "raw-window-handle"] } +wgpu = "^0.19" +pollster = "^0.3" diff --git a/wgpu/README.md b/flwgpu/README.md similarity index 100% rename from wgpu/README.md rename to flwgpu/README.md diff --git a/wgpu/src/main.rs b/flwgpu/src/main.rs similarity index 100% rename from wgpu/src/main.rs rename to flwgpu/src/main.rs diff --git a/wgpu/src/shader.wgsl b/flwgpu/src/shader.wgsl similarity index 100% rename from wgpu/src/shader.wgsl rename to flwgpu/src/shader.wgsl diff --git a/wgpu/wgpu.gif b/flwgpu/wgpu.gif similarity index 100% rename from wgpu/wgpu.gif rename to flwgpu/wgpu.gif diff --git a/framebuffer/Cargo.toml b/framebuffer/Cargo.toml index 73be3c6..169bf6b 100644 --- a/framebuffer/Cargo.toml +++ b/framebuffer/Cargo.toml @@ -7,4 +7,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1" +fltk = "^1.4" diff --git a/framebuffer/src/main.rs b/framebuffer/src/main.rs index dbffefe..0ad11ce 100644 --- a/framebuffer/src/main.rs +++ b/framebuffer/src/main.rs @@ -1,5 +1,4 @@ use fltk::{app, draw, frame, prelude::*, window::Window}; -use std::{thread, time::Duration}; const WIDTH: u32 = 600; const HEIGHT: u32 = 400; diff --git a/glut/Cargo.toml b/glut/Cargo.toml index a77f223..c417813 100644 --- a/glut/Cargo.toml +++ b/glut/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -gl = "0.14" -fltk = { version = "1", features = ["enable-glwindow"] } +gl = "^0.14" +fltk = { version = "^1.4", features = ["enable-glwindow"] } diff --git a/glut/src/main.rs b/glut/src/main.rs index 5dc7173..6b46604 100644 --- a/glut/src/main.rs +++ b/glut/src/main.rs @@ -1,23 +1,19 @@ use fltk::{prelude::*, *}; - use gl::types::*; -use std::ffi::CString; -use std::mem; -use std::ptr; -use std::str; +use std::{ffi::CString, mem, ptr, str}; // Vertex data static VERTEX_DATA: [GLfloat; 6] = [0.0, 0.5, 0.5, -0.5, -0.5, -0.5]; // Shader sources -static VS_SRC: &'static str = " +static VS_SRC: &str = " #version 150 in vec2 position; void main() { gl_Position = vec4(position, 0.0, 1.0); }"; -static FS_SRC: &'static str = " +static FS_SRC: &str = " #version 150 out vec4 out_color; void main() { @@ -41,8 +37,7 @@ fn compile_shader(src: &str, ty: GLenum) -> GLuint { if status != (gl::TRUE as GLint) { let mut len = 0; gl::GetShaderiv(shader, gl::INFO_LOG_LENGTH, &mut len); - let mut buf = Vec::with_capacity(len as usize); - buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character + let mut buf: Vec = vec![0; (len as usize) - 1]; gl::GetShaderInfoLog( shader, len, @@ -51,9 +46,7 @@ fn compile_shader(src: &str, ty: GLenum) -> GLuint { ); panic!( "{}", - str::from_utf8(&buf) - .ok() - .expect("ShaderInfoLog not valid utf8") + str::from_utf8(&buf).expect("ShaderInfoLog not valid utf8") ); } } @@ -74,8 +67,7 @@ fn link_program(vs: GLuint, fs: GLuint) -> GLuint { if status != (gl::TRUE as GLint) { let mut len: GLint = 0; gl::GetProgramiv(program, gl::INFO_LOG_LENGTH, &mut len); - let mut buf = Vec::with_capacity(len as usize); - buf.set_len((len as usize) - 1); // subtract 1 to skip the trailing null character + let mut buf: Vec = vec![0; (len as usize) - 1]; gl::GetProgramInfoLog( program, len, @@ -84,9 +76,7 @@ fn link_program(vs: GLuint, fs: GLuint) -> GLuint { ); panic!( "{}", - str::from_utf8(&buf) - .ok() - .expect("ProgramInfoLog not valid utf8") + str::from_utf8(&buf).expect("ProgramInfoLog not valid utf8") ); } program @@ -94,7 +84,6 @@ fn link_program(vs: GLuint, fs: GLuint) -> GLuint { } fn main() { - let app = app::App::default(); let mut win = window::GlWindow::new(100, 100, 500, 400, ""); win.make_resizable(true); win.set_mode(enums::Mode::Opengl3); @@ -122,7 +111,7 @@ fn main() { gl::BufferData( gl::ARRAY_BUFFER, (VERTEX_DATA.len() * mem::size_of::()) as GLsizeiptr, - mem::transmute(&VERTEX_DATA[0]), + &VERTEX_DATA[0] as *const f32 as *const std::ffi::c_void, gl::STATIC_DRAW, ); @@ -154,5 +143,5 @@ fn main() { g.swap_buffers(); }); - app.run().unwrap(); + app::App::default().run().unwrap(); } diff --git a/glyphmap/Cargo.toml b/glyphmap/Cargo.toml index 28ff21c..7951d47 100644 --- a/glyphmap/Cargo.toml +++ b/glyphmap/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1.2" -ttf-parser = "0.14" \ No newline at end of file +fltk = "^1.4" +ttf-parser = "0.14" diff --git a/glyphmap/src/main.rs b/glyphmap/src/main.rs index e2448a4..a2e8aef 100644 --- a/glyphmap/src/main.rs +++ b/glyphmap/src/main.rs @@ -2,7 +2,6 @@ extern crate ttf_parser; use fltk::{enums::*, prelude::*, *}; fn main() { - let app = app::App::default(); app::set_background_color(170, 189, 206); dialog::message_title_default("Glyph Map"); let mut wind = window::Window::default() @@ -56,8 +55,7 @@ fn main() { let c = char::from_u32(codepoint).unwrap(); let txt = String::from(c); let (w, h) = draw::measure(&txt, true); - if w != 0 && h != 0 { - if face.glyph_index(c).is_some() { + if w != 0 && h != 0 && face.glyph_index(c).is_some() { let hpack = group::Pack::default() .with_type(group::PackType::Horizontal) .with_size(0, 50); @@ -76,7 +74,6 @@ fn main() { out.set_value(&txt); hpack.end(); } - } }); } pack.end(); @@ -90,5 +87,5 @@ fn main() { } }); - app.run().unwrap(); + app::App::default().run().unwrap(); } diff --git a/gst/Cargo.toml b/gst/Cargo.toml index cc6911d..a4c1663 100644 --- a/gst/Cargo.toml +++ b/gst/Cargo.toml @@ -7,6 +7,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = " 1" +fltk = "^1.4" gstreamer = "0.17" -gstreamer-video = "0.17" \ No newline at end of file +gstreamer-video = "0.17" diff --git a/gst/src/main.rs b/gst/src/main.rs index b6ab71a..5378f4a 100644 --- a/gst/src/main.rs +++ b/gst/src/main.rs @@ -32,7 +32,7 @@ fn main() { let mut path = String::from("file:///"); let current_dir = std::env::current_dir().unwrap(); let video_file = current_dir.join(uri); - path += &video_file.to_str().unwrap(); + path += video_file.to_str().unwrap(); let playbin = gstreamer::ElementFactory::make("playbin", None).unwrap(); playbin.set_property("uri", &path).unwrap(); diff --git a/libvlc/Cargo.toml b/libvlc/Cargo.toml index 77eb046..67faade 100644 --- a/libvlc/Cargo.toml +++ b/libvlc/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1" +fltk = "^1.4" vlc-rs = { git = "https://github.com/garkimasera/vlc-rs.git" } diff --git a/libvlc/src/main.rs b/libvlc/src/main.rs index 6fb964e..6d49764 100644 --- a/libvlc/src/main.rs +++ b/libvlc/src/main.rs @@ -8,7 +8,7 @@ pub enum Message { } fn main() { - let app = app::App::default().with_scheme(app::AppScheme::Gtk); + let (sender, receiver) = app::channel::(); let mut win = window::Window::new(100, 100, 800, 600, "Media Player"); win.make_resizable(true); @@ -17,21 +17,17 @@ fn main() { vlc_win.end(); vlc_win.set_color(Color::Black); - let mut but_play = button::Button::new(320, 545, 80, 40, "Play"); - let mut but_stop = button::Button::new(400, 545, 80, 40, "Stop"); + button::Button::new(320, 545, 80, 40, "Play").emit(sender, Message::Play); + button::Button::new(400, 545, 80, 40, "Stop").emit(sender, Message::Stop); win.end(); win.show(); win.make_resizable(true); - // Take in same args as vlc - let args: Vec = std::env::args().collect(); - // Instantiate vlc instance and media player let instance = Instance::new().unwrap(); - let md = Media::new_path(&instance, "video.mp4").unwrap(); let mdp = MediaPlayer::new(&instance).unwrap(); - mdp.set_media(&md); + mdp.set_media(&Media::new_path(&instance, "video.mp4").unwrap()); // Get vlc_win handle that we'll pass to libvlc // Linux u32, windows HWND, Mac NSWindow @@ -54,13 +50,8 @@ fn main() { mdp.set_key_input(false); mdp.set_mouse_input(false); - let (s, r) = app::channel::(); - - but_play.emit(s, Message::Play); - but_stop.emit(s, Message::Stop); - - while app.wait() { - match r.recv() { + while app::App::default().with_scheme(app::AppScheme::Gtk).wait() { + match receiver.recv() { Some(val) => match val { Message::Play => mdp.play().unwrap(), Message::Stop => mdp.stop(), diff --git a/mpv/Cargo.toml b/mpv/Cargo.toml index f791e6c..7c7c134 100644 --- a/mpv/Cargo.toml +++ b/mpv/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1" \ No newline at end of file +fltk = "^1.4" diff --git a/musicplayer/Cargo.toml b/musicplayer/Cargo.toml index 3255478..9447ec9 100644 --- a/musicplayer/Cargo.toml +++ b/musicplayer/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1.3.33" -soloud = "1.0.2" +fltk = "^1.4" +soloud = "^1.0" diff --git a/musicplayer/src/main.rs b/musicplayer/src/main.rs index 5e062c4..6165b0b 100644 --- a/musicplayer/src/main.rs +++ b/musicplayer/src/main.rs @@ -1,7 +1,6 @@ use fltk::{app, enums::*, frame::*, prelude::*, window::*}; use soloud::*; -use std::cell::RefCell; -use std::rc::Rc; +use std::{cell::RefCell,rc::Rc}; mod power_button; use power_button::PowerButton; diff --git a/musicplayer/src/power_button.rs b/musicplayer/src/power_button.rs index 762aca8..3f3fa2e 100644 --- a/musicplayer/src/power_button.rs +++ b/musicplayer/src/power_button.rs @@ -1,9 +1,9 @@ -use fltk::frame::*; -use fltk::image::*; -use fltk::{enums::*, prelude::*}; -use std::cell::RefCell; -use std::ops::{Deref, DerefMut}; -use std::rc::Rc; +use fltk::{enums::*, frame::*, image::*, prelude::*}; +use std::{ + cell::RefCell, + ops::{Deref, DerefMut}, + rc::Rc, +}; const POWER: &str = r#" diff --git a/opengl/Cargo.toml b/opengl/Cargo.toml index 2943956..45ccc8b 100644 --- a/opengl/Cargo.toml +++ b/opengl/Cargo.toml @@ -7,6 +7,6 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = { version = "1", features = ["enable-glwindow"] } +fltk = { version = "^1.4", features = ["enable-glwindow"] } glu-sys = "0.1.4" diff --git a/opengl/src/main.rs b/opengl/src/main.rs index 0f84779..1ba8a19 100644 --- a/opengl/src/main.rs +++ b/opengl/src/main.rs @@ -1,13 +1,11 @@ use fltk::{prelude::*, *}; use glu_sys::*; -use std::cell::RefCell; -use std::rc::Rc; +use std::{cell::RefCell, rc::Rc}; const W: i32 = 600; const H: i32 = 400; pub fn main() { - let app = app::App::default(); let mut wind = window::GlWindow::new(100, 100, W, H, "Rotate me!"); wind.make_resizable(true); wind.end(); @@ -28,7 +26,7 @@ pub fn main() { _ => false, }); - while app.wait() { + while app::App::default().wait() { match r.recv() { Some(coords) => { let rand: f32 = ((coords.0 - W / 2) * (coords.1 - H / 2) / 360) as f32; diff --git a/rounded-svg/Cargo.toml b/rounded-svg/Cargo.toml index 1b24a82..da67cab 100644 --- a/rounded-svg/Cargo.toml +++ b/rounded-svg/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1" -svg = "0.10" \ No newline at end of file +fltk = "^1.4" +svg = "0.10" diff --git a/systray/Cargo.toml b/systray/Cargo.toml index 731ba8c..506434e 100644 --- a/systray/Cargo.toml +++ b/systray/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1" +fltk = "^1.4" [target.'cfg(target_os = "windows")'.dependencies] -native-windows-gui = { version = "1.0.10", default-features=false, features=["tray-notification", "message-window", "menu", "cursor"] } \ No newline at end of file +native-windows-gui = { version = "1.0.10", default-features=false, features=["tray-notification", "message-window", "menu", "cursor"] } diff --git a/systray/src/main.rs b/systray/src/main.rs index e316589..ef0b5af 100644 --- a/systray/src/main.rs +++ b/systray/src/main.rs @@ -3,8 +3,8 @@ use fltk::{app, enums::FrameType, prelude::*, *}; #[cfg(target_os = "windows")] mod systray; -type HWND = *mut std::os::raw::c_void; -pub static mut WINDOW: HWND = std::ptr::null_mut(); +type Hwnd = *mut std::os::raw::c_void; +pub static mut WINDOW: Hwnd = std::ptr::null_mut(); fn main() { let app = app::App::default(); diff --git a/systray/src/systray.rs b/systray/src/systray.rs index 44478ad..4aa8567 100644 --- a/systray/src/systray.rs +++ b/systray/src/systray.rs @@ -19,7 +19,7 @@ impl SystemTray { fn show_main_win(&self) { extern "C" { - pub fn ShowWindow(hwnd: crate::HWND, nCmdShow: i32) -> bool; + pub fn ShowWindow(hwnd: crate::Hwnd, nCmdShow: i32) -> bool; } unsafe { ShowWindow(crate::WINDOW, 9); diff --git a/terminal/Cargo.toml b/terminal/Cargo.toml index 314f654..063c8f4 100644 --- a/terminal/Cargo.toml +++ b/terminal/Cargo.toml @@ -6,5 +6,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1.3" -portable-pty = "0.7" \ No newline at end of file +fltk = "^1.4" +portable-pty = "0.7" diff --git a/tinyskia/Cargo.toml b/tinyskia/Cargo.toml index 238fffa..1e1ffe8 100644 --- a/tinyskia/Cargo.toml +++ b/tinyskia/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1" -tiny-skia = "0.5" \ No newline at end of file +fltk = "^1.4" +tiny-skia = "0.5" diff --git a/tinyskia/src/main.rs b/tinyskia/src/main.rs index 7eca499..3991385 100644 --- a/tinyskia/src/main.rs +++ b/tinyskia/src/main.rs @@ -4,15 +4,17 @@ use tiny_skia::*; fn main() { let triangle = create_triangle(); - let mut paint = Paint::default(); - paint.anti_alias = true; - paint.shader = Pattern::new( - triangle.as_ref(), - SpreadMode::Repeat, - FilterQuality::Bicubic, - 1.0, - Transform::from_row(1.5, -0.4, 0.0, -0.8, 5.0, 1.0), - ); + let paint = Paint { + anti_alias: true, + shader: Pattern::new( + triangle.as_ref(), + SpreadMode::Repeat, + FilterQuality::Bicubic, + 1.0, + Transform::from_row(1.5, -0.4, 0.0, -0.8, 5.0, 1.0), + ), + .. Paint::default() + }; let path = PathBuilder::from_circle(200.0, 200.0, 180.0).unwrap(); diff --git a/web-todo/Cargo.toml b/web-todo/Cargo.toml index cbfed4f..19c1357 100644 --- a/web-todo/Cargo.toml +++ b/web-todo/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1.3.33" +fltk = "^1.4" serde = { version = "1.0", features = ["derive"] } reqwest = { version = "0.11", features = ["json"] } tokio = { version = "1.0", features = ["full"] } diff --git a/web-todo2/Cargo.toml b/web-todo2/Cargo.toml index 6f5952a..545eeba 100644 --- a/web-todo2/Cargo.toml +++ b/web-todo2/Cargo.toml @@ -7,7 +7,7 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1" +fltk = "^1.4" serde = { version = "1.0", features = ["derive"] } surf = { version = "2.1.0" } async-std = { version = "1.9", features = ["attributes"] } diff --git a/webview/Cargo.toml b/webview/Cargo.toml index d1ad21c..92bfdbc 100644 --- a/webview/Cargo.toml +++ b/webview/Cargo.toml @@ -7,5 +7,5 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1" -fltk-webview = "0.2" +fltk = "^1.4" +fltk-webview = "^0.3" diff --git a/webview/src/main.rs b/webview/src/main.rs index 7f56d0f..1c112f0 100644 --- a/webview/src/main.rs +++ b/webview/src/main.rs @@ -1,7 +1,6 @@ -use fltk::{app, enums::Event, prelude::*, window}; +use fltk::{app, prelude::*, window}; fn main() { - let app = app::App::default(); let mut win = window::Window::default() .with_size(730, 430) .with_label("Webview"); @@ -12,9 +11,9 @@ fn main() { win.end(); win.show(); - let mut wv = fltk_webview::Webview::create(false, &mut wv_win); + let wv = fltk_webview::Webview::create(false, &mut wv_win); wv.navigate("https://google.com"); // the webview handles the main loop - app.run().unwrap(); + app::App::default().run().unwrap(); } diff --git a/xterm/Cargo.toml b/xterm/Cargo.toml index 0c8fed7..a59d604 100644 --- a/xterm/Cargo.toml +++ b/xterm/Cargo.toml @@ -6,4 +6,4 @@ edition = "2021" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html [dependencies] -fltk = "1" +fltk = "^1.4" diff --git a/xterm/src/main.rs b/xterm/src/main.rs index 73db4fc..972b009 100644 --- a/xterm/src/main.rs +++ b/xterm/src/main.rs @@ -14,11 +14,10 @@ fn main() { win.show(); win.make_resizable(true); - let mut handle = xterm_win.raw_handle(); std::process::Command::new("xterm") .args(&[ "-into", - &format!("{}", handle), + &format!("{}", xterm_win.raw_handle()), "-bg", "black", "-fg",