Skip to content

Commit

Permalink
feat: working egui app
Browse files Browse the repository at this point in the history
  • Loading branch information
nekowinston committed May 12, 2023
1 parent 27452f1 commit db48e2e
Show file tree
Hide file tree
Showing 11 changed files with 325 additions and 121 deletions.
26 changes: 26 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,12 @@ deltae = "0.3.0"
dither = { path = "dither" }
faerber = { path = "faerber" }
faerber_lib = { path = "faerber_lib" }
oxipng = "0.8.0"
image = "0.24.5"
lazy_static = "1.4.0"
oxipng = "0.8.0"
serde = "1.0.162"
serde_json = "1.0.85"
strum = { version = "0.24", features = ["derive"] }
thiserror = "1.0.40"

[profile.release]
Expand Down
1 change: 1 addition & 0 deletions app/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ rfd = "0.11.1"
serde = { workspace = true, features = ["derive"] }
serde_json.workspace = true
sublime_fuzzy = "0.7.0"
strum.workspace = true

[dependencies.eframe]
version = "0.21.3"
Expand Down
217 changes: 167 additions & 50 deletions app/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
#![warn(
// clippy::cargo,
// clippy::expect_used,
// clippy::pedantic,
// clippy::unwrap_used,
clippy::complexity,
clippy::nursery,
clippy::perf,
clippy::style,
clippy::suspicious,
)]
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] // hide console window on Windows in release

use catppuccin_egui::{set_theme, LATTE, MOCHA};
use colorschemes::LibraryManager;
use eframe::egui;
use egui::{Sense, Stroke, Vec2};
use egui::{ColorImage, Sense, Stroke, Vec2};
use egui_extras::RetainedImage;
use rfd::FileDialog;
use std::{fs, path::PathBuf};
use strum::IntoEnumIterator;
use sublime_fuzzy::best_match;

fn main() -> Result<(), eframe::Error> {
Expand All @@ -15,33 +27,33 @@ fn main() -> Result<(), eframe::Error> {
..Default::default()
};

eframe::run_native(
"farbenfroh.io - faerber",
options,
Box::new(|_cc| Box::<MyApp>::default()),
)
eframe::run_native("faerber", options, Box::new(|_cc| Box::<MyApp>::default()))
}

struct MyApp {
image: Option<RetainedImage>,
// original_image: Option<RetainedImage>,
// converted_image: Option<RetainedImage>,
converted_image: Option<RetainedImage>,
converted_image_src: Option<image::RgbaImage>,
opened_file: Option<PathBuf>,
library: LibraryManager,
color_scheme: String,
flavor: String,
flavor_filter: String,
method: faerber_lib::ConversionMethod,
}

impl Default for MyApp {
fn default() -> Self {
Self {
image: None,
converted_image: None,
converted_image_src: None,
opened_file: None,
library: LibraryManager::new(),
color_scheme: "wezterm".to_string(),
flavor: "catppuccin mocha".to_string(),
flavor_filter: "".to_string(),
method: faerber_lib::ConversionMethod::De1976,
}
}
}
Expand All @@ -50,24 +62,80 @@ impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
match frame.info().system_theme {
Some(eframe::Theme::Dark) => set_theme(ctx, MOCHA),
Some(eframe::Theme::Light) => set_theme(ctx, LATTE),
None => set_theme(ctx, LATTE),
_ => set_theme(ctx, LATTE),
}
egui::CentralPanel::default().show(ctx, |ui| {
if ui.button("Open").clicked() {
self.opened_file = FileDialog::new()
.add_filter("Images", &["png", "jpg", "jpeg"])
.add_filter("Vector files", &["svg"])
.pick_file();
}

egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
ui.horizontal_top(|ui| {
ui.menu_button("File", |ui| self.file_menu(ui));
if ui.button("Convert").clicked() {
if let Some(file) = &self.opened_file {
let img = image::open(file).unwrap();
self.image = None;
let x = match self.method {
faerber_lib::ConversionMethod::De1976
| faerber_lib::ConversionMethod::De1994T
| faerber_lib::ConversionMethod::De1994G
| faerber_lib::ConversionMethod::De2000 => faerber_lib::convert_naive(
&img.to_rgba8(),
self.method,
self.library
.get("wezterm")
.unwrap()
.get(&self.flavor)
.unwrap()
.get_labs()
.as_slice(),
),
faerber_lib::ConversionMethod::DitherFloydSteinberg
| faerber_lib::ConversionMethod::DitherAtkinson
| faerber_lib::ConversionMethod::DitherStucki
| faerber_lib::ConversionMethod::DitherBurkes
| faerber_lib::ConversionMethod::DitherJarvisJudiceNinke
| faerber_lib::ConversionMethod::DitherSierra3 => {
faerber_lib::convert_dither(
&img.to_rgba8(),
self.method,
self.library
.get("wezterm")
.unwrap()
.get(&self.flavor)
.unwrap()
.get_u32s()
.as_slice(),
)
}
};
let y = x.expect("Conversion failed");
self.converted_image_src = Some(
image::RgbaImage::from_vec(img.width(), img.height(), y.clone())
.unwrap(),
);
let ret_img = RetainedImage::from_color_image(
"converted_image",
ColorImage::from_rgba_unmultiplied(
[
usize::try_from(img.width()).unwrap(),
usize::try_from(img.height()).unwrap(),
],
y.as_slice(),
),
);
self.converted_image = Some(ret_img)
}
}
})
});

egui::CentralPanel::default().show(ctx, |ui| {
if let Some(file) = &mut self.opened_file {
let is_svg = file.extension().unwrap() == "svg";
let data = fs::read(file).unwrap();
self.image = Some(RetainedImage::from_image_bytes("image", &data).unwrap());
}

if let Some(image) = &self.image {
image.show_max_size(ui, egui::vec2(300.0, 300.0));
self.image = if is_svg {
Some(RetainedImage::from_svg_bytes("image", &data).unwrap())
} else {
Some(RetainedImage::from_image_bytes("image", &data).unwrap())
}
}

ui.separator();
Expand All @@ -91,55 +159,58 @@ impl eframe::App for MyApp {
}
});
ui.horizontal(|ui| {
self.library
.library
.get(&self.color_scheme)
.unwrap()
.keys()
.filter_map(|f| {
if self.flavor_filter.is_empty() {
Some(f)
} else {
best_match(&self.flavor_filter, f).map(|_| f)
}
})
.for_each(|flavor| {
ui.selectable_value(&mut self.flavor, flavor.to_string(), flavor);
egui::ComboBox::from_label("")
.selected_text(&self.flavor)
.show_ui(ui, |ui| {
self.library
.library
.get(&self.color_scheme)
.unwrap()
.keys()
.filter_map(|f| {
if self.flavor_filter.is_empty() {
Some(f)
} else {
best_match(&self.flavor_filter, f).map(|_| f)
}
})
.for_each(|flavor| {
ui.selectable_value(&mut self.flavor, flavor.to_string(), flavor);
});
});
if ui.button("+").clicked() {}
});
ui.label("Colors");
ui.horizontal(|ui| {
let library = self.library.clone();
ui.horizontal_wrapped(|ui| {
let library = &self.library;
library
.library
.get(&self.color_scheme)
.unwrap()
.get(&self.flavor)
.unwrap()
.palette
.get_colors()
.iter()
.for_each(|color| {
let r = 10.0;
let size = Vec2::splat(2.0 * r + 5.0);
let size = Vec2::splat(2.0f32.mul_add(r, 5.0));
let (rect, sense) = ui
.allocate_at_least(size, Sense::union(Sense::hover(), Sense::click()));

let hint_text = format!("{}: {}", color.1.name, color.1.enabled);
let hint_text = format!("{}: {}", color.name, color.enabled);
sense
.clone()
.on_hover_cursor(egui::CursorIcon::PointingHand)
.on_hover_text(hint_text);

let cv = color.1.value;
let cv = color.value;
let c_view = egui::Color32::from_rgba_premultiplied(
(cv >> 16 & 0xFF) as u8,
(cv >> 8 & 0xFF) as u8,
(cv & 0xFF) as u8,
255,
);

let c = match color.1.enabled {
let c = match color.enabled {
true => c_view,
false => c_view.gamma_multiply(0.3),
};
Expand All @@ -149,18 +220,64 @@ impl eframe::App for MyApp {
.set_color(
&self.color_scheme,
&self.flavor,
color.0,
!color.1.enabled,
&color.name,
!color.enabled,
)
.unwrap();
}

// ui.painter().circle_filled(rect.center(), r, c);
ui.painter().rect_filled(rect, 0.0, c);
ui.painter()
.rect_stroke(rect, 0.0, Stroke::new(1.0, egui::Color32::BLACK));
match color.enabled {
true => ui.painter().circle_filled(rect.center(), r, c),
false => ui.painter().circle(
rect.center(),
r,
egui::Color32::TRANSPARENT,
Stroke::new(1.0, c),
),
};
});
});
ui.label("Conversion Method");
ui.horizontal_wrapped(|ui| {
// for each key of ConversionMethod, create a button changing the conversion method
faerber_lib::ConversionMethod::iter().for_each(|method| {
ui.selectable_value(&mut self.method, method, method.to_string());
});
});
ui.separator();
if let Some(img) = &self.image {
img.show_max_size(ui, egui::vec2(ui.available_width(), 300.0));
}
if let Some(img) = &self.converted_image {
img.show_max_size(ui, egui::vec2(ui.available_width(), 300.0));
}
});
}
}

impl MyApp {
fn file_menu(&mut self, ui: &mut egui::Ui) {
if ui.button("Open").clicked() {
ui.close_menu();
self.opened_file = FileDialog::new()
.add_filter("Images", &["png", "jpg", "jpeg"])
.add_filter("Vector files", &["svg"])
.pick_file();
}
ui.add_enabled_ui(self.converted_image_src.is_some(), |ui| {
if ui.button("Save").clicked() {
let path = FileDialog::new()
.add_filter("Images", &["png", "jpg", "jpeg"])
.add_filter("Vector files", &["svg"])
.save_file();
if let Some(path) = path {
_ = self
.converted_image_src
.as_ref()
.unwrap()
.save_with_format(&path, image::ImageFormat::from_path(&path).unwrap());
}
}
});
}
}
Loading

0 comments on commit db48e2e

Please sign in to comment.