mirror of https://github.com/Leinnan/rpack.git
Update EGUI
This commit is contained in:
parent
9bd2a2bd5b
commit
8db4d5b2bc
|
|
@ -2,7 +2,7 @@
|
|||
name = "bevy_rpack"
|
||||
description = "Bevy plugin with rpack atlas support"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
repository = "https://github.com/Leinnan/rpack.git"
|
||||
homepage = "https://github.com/Leinnan/rpack"
|
||||
authors = ["Piotr Siuszko <siuszko@zoho.com>"]
|
||||
|
|
@ -21,7 +21,7 @@ bevy = { version = "0.16", optional = true, default-features = false, features =
|
|||
"bevy_image",
|
||||
"bevy_ui"
|
||||
] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
thiserror = "2"
|
||||
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ repository = "https://github.com/Leinnan/rpack.git"
|
|||
homepage = "https://github.com/Leinnan/rpack"
|
||||
license = "MIT OR Apache-2.0"
|
||||
version = "0.2.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[features]
|
||||
default = ["cli", "dds"]
|
||||
|
|
|
|||
|
|
@ -1,24 +1,23 @@
|
|||
[package]
|
||||
name = "rpack_egui"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
description = "GUI application for generating rpack atlases"
|
||||
authors = ["Piotr Siuszko <siuszko@zoho.com>"]
|
||||
edition = "2021"
|
||||
rust-version = "1.81"
|
||||
edition = "2024"
|
||||
repository = "https://github.com/Leinnan/rpack.git"
|
||||
homepage = "https://github.com/Leinnan/rpack"
|
||||
license = "MIT OR Apache-2.0"
|
||||
|
||||
[dependencies]
|
||||
egui = "0.30"
|
||||
eframe = { version = "0.30", default-features = false, features = [
|
||||
egui = "0.32"
|
||||
eframe = { version = "0.32", default-features = false, features = [
|
||||
"accesskit", # Make egui comptaible with screen readers. NOTE: adds a lot of dependencies.
|
||||
"default_fonts", # Embed the default egui fonts.
|
||||
"glow", # Use the glow rendering backend. Alternative: "wgpu".
|
||||
"persistence", # Enable restoring app state when restarting the app.
|
||||
] }
|
||||
log = "0.4"
|
||||
egui_json_tree = "0.10"
|
||||
egui_json_tree = "0.13"
|
||||
rpack_cli = { default-features = false, path = "../rpack_cli", version = "0.2" }
|
||||
|
||||
# You only need serde if you want app persistence:
|
||||
|
|
@ -26,7 +25,7 @@ serde = { version = "1", features = ["derive"] }
|
|||
serde_json = "1"
|
||||
texture_packer = { workspace = true }
|
||||
image = { workspace = true }
|
||||
egui_extras = { version = "0.30", features = ["all_loaders"] }
|
||||
egui_extras = { version = "0.32", features = ["all_loaders"] }
|
||||
rfd = { version = "0.15", features = [] }
|
||||
wasm-bindgen-futures = "0.4"
|
||||
anyhow = "1"
|
||||
|
|
|
|||
|
|
@ -1,10 +0,0 @@
|
|||
# If you see this, run "rustup self update" to get rustup 1.23 or newer.
|
||||
|
||||
# NOTE: above comment is for older `rustup` (before TOML support was added),
|
||||
# which will treat the first line as the toolchain name, and therefore show it
|
||||
# to the user in the error, instead of "error: invalid channel name '[toolchain]'".
|
||||
|
||||
[toolchain]
|
||||
channel = "1.81"
|
||||
components = [ "rustfmt", "clippy" ]
|
||||
targets = [ "wasm32-unknown-unknown" ]
|
||||
|
|
@ -1,7 +1,8 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
use egui::{CollapsingHeader, Color32, DroppedFile, FontFamily, FontId, Image, RichText};
|
||||
use image::GenericImageView;
|
||||
use egui::{
|
||||
CollapsingHeader, Color32, DroppedFile, FontFamily, FontId, Grid, Image, Label, RichText,
|
||||
};
|
||||
use rpack_cli::{ImageFile, Spritesheet, SpritesheetError};
|
||||
use texture_packer::TexturePackerConfig;
|
||||
|
||||
|
|
@ -33,8 +34,22 @@ pub struct Application {
|
|||
#[serde(skip)]
|
||||
max_size: u32,
|
||||
#[serde(skip)]
|
||||
image_data: HashMap<String, ImageFile>,
|
||||
image_data: HashMap<String, AppImageData>,
|
||||
}
|
||||
|
||||
pub struct AppImageData {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub data: ImageFile,
|
||||
pub path: String,
|
||||
}
|
||||
|
||||
impl AppImageData {
|
||||
pub fn id(&self) -> &str {
|
||||
self.data.id.as_str()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for Application {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
|
|
@ -71,7 +86,7 @@ impl Application {
|
|||
self.image_data = self
|
||||
.dropped_files
|
||||
.iter()
|
||||
.flat_map(|f| f.create_image(&prefix))
|
||||
.flat_map(|f| f.create_image(&prefix).map(|i| (i.id().to_string(), i)))
|
||||
.collect();
|
||||
self.update_min_size();
|
||||
}
|
||||
|
|
@ -79,18 +94,18 @@ impl Application {
|
|||
if let Some(file) = self
|
||||
.image_data
|
||||
.values()
|
||||
.max_by(|a, b| a.image.width().cmp(&b.image.width()))
|
||||
.max_by(|a, b| a.width.cmp(&b.width))
|
||||
{
|
||||
self.min_size[0] = file.image.width();
|
||||
self.min_size[0] = file.width;
|
||||
} else {
|
||||
self.min_size[0] = 32;
|
||||
}
|
||||
if let Some(file) = self
|
||||
.image_data
|
||||
.values()
|
||||
.max_by(|a, b| a.image.height().cmp(&b.image.height()))
|
||||
.max_by(|a, b| a.height.cmp(&b.height))
|
||||
{
|
||||
self.min_size[1] = file.image.height();
|
||||
self.min_size[1] = file.height;
|
||||
} else {
|
||||
self.min_size[1] = 32;
|
||||
}
|
||||
|
|
@ -114,7 +129,11 @@ impl Application {
|
|||
fn build_atlas(&mut self, ctx: &egui::Context) {
|
||||
self.data = None;
|
||||
self.image = None;
|
||||
let images: Vec<ImageFile> = self.image_data.values().cloned().collect();
|
||||
let images: Vec<ImageFile> = self
|
||||
.image_data
|
||||
.values()
|
||||
.map(|file| file.data.clone())
|
||||
.collect();
|
||||
|
||||
for size in [32, 64, 128, 256, 512, 1024, 2048, 4096] {
|
||||
if size < self.min_size[0] || size < self.min_size[1] {
|
||||
|
|
@ -328,25 +347,41 @@ impl eframe::App for Application {
|
|||
CollapsingHeader::new("Image list")
|
||||
.default_open(true)
|
||||
.show(ui, |ui| {
|
||||
ui.horizontal(|ui|{
|
||||
|
||||
if !self.image_data.is_empty() && ui.button("clear list").clicked() {
|
||||
self.image_data.clear();
|
||||
self.dropped_files.clear();
|
||||
self.data = None;
|
||||
self.update_min_size();
|
||||
}
|
||||
let mut to_remove: Option<String> = None;
|
||||
for (id, file) in self.image_data.iter() {
|
||||
ui.horizontal_top(|ui| {
|
||||
ui.add_space(10.0);
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
if ui.button("Add").clicked() {
|
||||
if let Some(files) = rfd::FileDialog::new().set_title("Add images").add_filter("Images", &["png", "jpg", "jpeg","dds"]).pick_files(){
|
||||
for file in files.iter() {
|
||||
let Ok(image) = texture_packer::importer::ImageImporter::import_from_file(file) else { continue };
|
||||
let id = crate::helpers::id_from_path(&file.to_string_lossy());
|
||||
self.image_data.insert(file.to_string_lossy().to_string(), AppImageData { width: image.width(), height: image.height(), data: ImageFile { id: id, image }, path: file.to_string_lossy().to_string() });
|
||||
}
|
||||
self.update_min_size();
|
||||
}
|
||||
}
|
||||
});
|
||||
let mut to_remove: Option<String> = None;
|
||||
Grid::new("Image List").num_columns(4).striped(true).spacing((10.0,10.0)).show(ui, |ui|{
|
||||
|
||||
for (id, file) in self.image_data.iter() {
|
||||
if ui.button("x").clicked() {
|
||||
to_remove = Some(id.clone());
|
||||
}
|
||||
ui.add_space(10.0);
|
||||
let (x, y) = file.image.dimensions();
|
||||
ui.label(&file.id)
|
||||
.on_hover_text(format!("Dimensions: {}x{}", x, y));
|
||||
});
|
||||
|
||||
ui.image(format!("file://{}", file.path));
|
||||
ui.add(Label::new(file.id()).selectable(false));
|
||||
ui.add(Label::new(format!("{}x{}", file.width, file.height)).selectable(false));
|
||||
ui.end_row();
|
||||
}
|
||||
});
|
||||
if let Some(index) = to_remove {
|
||||
if let Some(i) = self
|
||||
.dropped_files
|
||||
|
|
@ -377,10 +412,13 @@ impl eframe::App for Application {
|
|||
if self.dropped_files.is_empty() {
|
||||
ui.vertical_centered_justified(|ui| {
|
||||
ui.add_space(50.0);
|
||||
ui.label(
|
||||
ui.add(
|
||||
Label::new(
|
||||
RichText::new("Drop images here first")
|
||||
.heading()
|
||||
.color(MY_ACCENT_COLOR32),
|
||||
)
|
||||
.selectable(false),
|
||||
);
|
||||
});
|
||||
}
|
||||
|
|
@ -444,10 +482,8 @@ impl eframe::App for Application {
|
|||
.add(egui::Button::new("Copy JSON to Clipboard"))
|
||||
.clicked()
|
||||
{
|
||||
ui.output_mut(|o| {
|
||||
o.copied_text =
|
||||
data.atlas_asset_json.to_string();
|
||||
});
|
||||
ui.ctx()
|
||||
.copy_text(data.atlas_asset_json.to_string());
|
||||
};
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -55,8 +55,8 @@ fn get_fonts() -> anyhow::Result<(Vec<u8>, Vec<u8>)> {
|
|||
fn get_fonts() -> anyhow::Result<(Vec<u8>, Vec<u8>)> {
|
||||
let font_path = std::path::Path::new("/System/Library/Fonts");
|
||||
|
||||
let regular = fs::read(font_path.join("SFNSRounded.ttf"))?;
|
||||
let semibold = fs::read(font_path.join("SFCompact.ttf"))?;
|
||||
let regular = std::fs::read(font_path.join("SFNSRounded.ttf"))?;
|
||||
let semibold = std::fs::read(font_path.join("SFCompact.ttf"))?;
|
||||
|
||||
Ok((regular, semibold))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,34 +3,37 @@ use image::DynamicImage;
|
|||
use rpack_cli::ImageFile;
|
||||
use texture_packer::importer::ImageImporter;
|
||||
|
||||
use crate::app::AppImageData;
|
||||
|
||||
pub trait DroppedFileHelper {
|
||||
fn file_path(&self) -> String;
|
||||
fn create_image<P>(&self, prefix: P) -> Option<(String, ImageFile)>
|
||||
fn create_image<P>(&self, prefix: P) -> Option<AppImageData>
|
||||
where
|
||||
P: AsRef<str>;
|
||||
fn dynamic_image(&self) -> Option<DynamicImage>;
|
||||
}
|
||||
|
||||
pub fn id_from_path(path: &str) -> String {
|
||||
match path.rfind('.') {
|
||||
Some(index) => path[..index].to_string(),
|
||||
None => path.to_string(),
|
||||
}
|
||||
.replace("\\", "/")
|
||||
}
|
||||
|
||||
impl DroppedFileHelper for DroppedFile {
|
||||
fn file_path(&self) -> String {
|
||||
let id;
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
{
|
||||
let path = self.path.as_ref().unwrap().clone();
|
||||
id = path.to_str().unwrap().to_owned();
|
||||
match self.path.as_ref() {
|
||||
Some(path) => path.to_string_lossy().to_string(),
|
||||
None => self.name.clone(),
|
||||
}
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
{
|
||||
id = self.name.clone();
|
||||
}
|
||||
id.replace(".png", "").replace("\\", "/")
|
||||
}
|
||||
fn create_image<P>(&self, prefix: P) -> Option<(String, ImageFile)>
|
||||
fn create_image<P>(&self, prefix: P) -> Option<AppImageData>
|
||||
where
|
||||
P: AsRef<str>,
|
||||
{
|
||||
let path = self.file_path();
|
||||
let base_id = path.replace(".png", "");
|
||||
let base_id = id_from_path(&path);
|
||||
|
||||
let id = base_id
|
||||
.strip_prefix(prefix.as_ref())
|
||||
|
|
@ -38,7 +41,12 @@ impl DroppedFileHelper for DroppedFile {
|
|||
.to_owned();
|
||||
|
||||
let image: DynamicImage = self.dynamic_image()?;
|
||||
Some((path, ImageFile { id, image }))
|
||||
Some(AppImageData {
|
||||
width: image.width(),
|
||||
height: image.height(),
|
||||
data: ImageFile { id: id, image },
|
||||
path,
|
||||
})
|
||||
}
|
||||
|
||||
fn dynamic_image(&self) -> Option<DynamicImage> {
|
||||
|
|
|
|||
Loading…
Reference in New Issue