From 89c81798b4fa9a80d170972c070892b31ce90b6c Mon Sep 17 00:00:00 2001 From: jaby Date: Tue, 21 Jan 2025 18:17:11 +0000 Subject: [PATCH 01/32] Create tim_tool project --- src/Tools/Tools.code-workspace | 1 + src/Tools/tim_tool/Cargo.toml | 9 +++++++++ src/Tools/tim_tool/Makefile | 13 +++++++++++++ src/Tools/tim_tool/src/lib.rs | 3 +++ src/Tools/tim_tool/src/main.rs | 3 +++ 5 files changed, 29 insertions(+) create mode 100644 src/Tools/tim_tool/Cargo.toml create mode 100644 src/Tools/tim_tool/Makefile create mode 100644 src/Tools/tim_tool/src/lib.rs create mode 100644 src/Tools/tim_tool/src/main.rs diff --git a/src/Tools/Tools.code-workspace b/src/Tools/Tools.code-workspace index 825be1e4..a9265339 100644 --- a/src/Tools/Tools.code-workspace +++ b/src/Tools/Tools.code-workspace @@ -13,6 +13,7 @@ "psxcdread all!psxcdread", "psxfileconv all!psxfileconv", "psxreadmap all!psxreadmap", + "tim_tool all!tim_tool", "wslpath all!wslpath", ] }, diff --git a/src/Tools/tim_tool/Cargo.toml b/src/Tools/tim_tool/Cargo.toml new file mode 100644 index 00000000..4db563ea --- /dev/null +++ b/src/Tools/tim_tool/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "tim_tool" +version = "0.1.0" +edition = "2021" + +[profile.release] +panic = "abort" + +[dependencies] diff --git a/src/Tools/tim_tool/Makefile b/src/Tools/tim_tool/Makefile new file mode 100644 index 00000000..f1694818 --- /dev/null +++ b/src/Tools/tim_tool/Makefile @@ -0,0 +1,13 @@ +include ../Common.mk + +ARTIFACT = tim_tool + +.PHONY: $(WINDOWS_ARTIFACT) $(UNIX_ARTIFACT) +$(WINDOWS_ARTIFACT): + $(call cargo_windows_default) + +$(UNIX_ARTIFACT): + $(call cargo_unix_default) + +all-windows: $(WINDOWS_ARTIFACT) +all: $(UNIX_ARTIFACT) \ No newline at end of file diff --git a/src/Tools/tim_tool/src/lib.rs b/src/Tools/tim_tool/src/lib.rs new file mode 100644 index 00000000..c1febacb --- /dev/null +++ b/src/Tools/tim_tool/src/lib.rs @@ -0,0 +1,3 @@ +pub fn hello_world() { + println!("Hello Planschi"); +} \ No newline at end of file diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs new file mode 100644 index 00000000..41bf1c4d --- /dev/null +++ b/src/Tools/tim_tool/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + tim_tool::hello_world(); +} \ No newline at end of file From 9549d011b81258ae72448357f70505b0be7f705c Mon Sep 17 00:00:00 2001 From: jaby Date: Wed, 22 Jan 2025 18:41:40 +0000 Subject: [PATCH 02/32] Hello Slint --- src/Tools/tim_tool/Cargo.toml | 4 ++++ src/Tools/tim_tool/build.rs | 3 +++ src/Tools/tim_tool/src/main.rs | 25 ++++++++++++++++++++++--- src/Tools/tim_tool/ui/app-window.slint | 19 +++++++++++++++++++ 4 files changed, 48 insertions(+), 3 deletions(-) create mode 100644 src/Tools/tim_tool/build.rs create mode 100644 src/Tools/tim_tool/ui/app-window.slint diff --git a/src/Tools/tim_tool/Cargo.toml b/src/Tools/tim_tool/Cargo.toml index 4db563ea..c695b90c 100644 --- a/src/Tools/tim_tool/Cargo.toml +++ b/src/Tools/tim_tool/Cargo.toml @@ -7,3 +7,7 @@ edition = "2021" panic = "abort" [dependencies] +slint = "1.9.2" + +[build-dependencies] +slint-build = "1.9.2" diff --git a/src/Tools/tim_tool/build.rs b/src/Tools/tim_tool/build.rs new file mode 100644 index 00000000..9bc30378 --- /dev/null +++ b/src/Tools/tim_tool/build.rs @@ -0,0 +1,3 @@ +fn main() { + slint_build::compile("ui/app-window.slint").expect("Slint build failed"); +} diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index 41bf1c4d..d065a6de 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -1,3 +1,22 @@ -fn main() { - tim_tool::hello_world(); -} \ No newline at end of file +// Prevent console window in addition to Slint window in Windows release builds when, e.g., starting the app via file manager. Ignored on other platforms. +#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] + +use std::error::Error; + +slint::include_modules!(); + +fn main() -> Result<(), Box> { + let ui = AppWindow::new()?; + + ui.on_request_increase_value({ + let ui_handle = ui.as_weak(); + move || { + let ui = ui_handle.unwrap(); + ui.set_counter(ui.get_counter() + 1); + } + }); + + ui.run()?; + + Ok(()) +} diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint new file mode 100644 index 00000000..a2cb9238 --- /dev/null +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -0,0 +1,19 @@ +import { Button, VerticalBox } from "std-widgets.slint"; + +export component AppWindow inherits Window { + in-out property counter: 42; + callback request-increase-value(); + VerticalBox { + Text { + text: "Counter: \{root.counter}"; + } + + Button { + checked: false; + text: "Increase value"; + clicked => { + root.request-increase-value(); + } + } + } +} From 93ac49f2c5921f312fd9787d165a7378423dd560 Mon Sep 17 00:00:00 2001 From: jaby Date: Thu, 23 Jan 2025 19:51:44 +0000 Subject: [PATCH 03/32] Sketch-out VRAM-Area --- src/Tools/tim_tool/src/main.rs | 20 +++-------- src/Tools/tim_tool/ui/app-window.slint | 49 ++++++++++++++++++-------- 2 files changed, 38 insertions(+), 31 deletions(-) diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index d065a6de..bf454cd4 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -1,22 +1,10 @@ // Prevent console window in addition to Slint window in Windows release builds when, e.g., starting the app via file manager. Ignored on other platforms. #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use std::error::Error; - slint::include_modules!(); -fn main() -> Result<(), Box> { - let ui = AppWindow::new()?; +fn main() -> Result<(), slint::PlatformError> { + let main_window = MainWindow::new()?; - ui.on_request_increase_value({ - let ui_handle = ui.as_weak(); - move || { - let ui = ui_handle.unwrap(); - ui.set_counter(ui.get_counter() + 1); - } - }); - - ui.run()?; - - Ok(()) -} + main_window.run() +} \ No newline at end of file diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint index a2cb9238..1b1b7399 100644 --- a/src/Tools/tim_tool/ui/app-window.slint +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -1,19 +1,38 @@ -import { Button, VerticalBox } from "std-widgets.slint"; +component VRAMSegment inherits Rectangle { + in property area_overlap_width; + in property area_overlap_height; -export component AppWindow inherits Window { - in-out property counter: 42; - callback request-increase-value(); - VerticalBox { - Text { - text: "Counter: \{root.counter}"; - } + width: 64px; + height: 256px; + background: #37f560; - Button { - checked: false; - text: "Increase value"; - clicked => { - root.request-increase-value(); - } - } + Rectangle { + x: 0px; + y: 0px; + width: area_overlap_width > 64 ? 64px : area_overlap_width*1px; + height: area_overlap_height > 256 ? 256px : area_overlap_height*1px; + background: #d7e609; } } + +component VRAMArea inherits Rectangle { + in property display_area_width; + in property display_area_height; + + for idx in 16 : VRAMSegment { + x: mod(idx,8)*(64+1)*1px; + y: floor(idx/8)*(256+1)*1px; + + area_overlap_width: parent.display_area_width - (mod(idx,8)*64); + area_overlap_height: parent.display_area_height - (256*floor(idx/8)); + } +} + +export component MainWindow inherits Window { + VRAMArea { + x: 0; + y: 0; + display_area_width: 32+64+3; + display_area_height: 256+128; + } +} \ No newline at end of file From 4b46683a9940eb5fe6f8ee972c8699c13befa7cb Mon Sep 17 00:00:00 2001 From: jaby Date: Sun, 26 Jan 2025 13:02:11 +0000 Subject: [PATCH 04/32] Adjust window size --- src/Tools/tim_tool/ui/app-window.slint | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint index 1b1b7399..2a4cfb17 100644 --- a/src/Tools/tim_tool/ui/app-window.slint +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -29,6 +29,9 @@ component VRAMArea inherits Rectangle { } export component MainWindow inherits Window { + min-width: 1024px; + min-height: 640px; + VRAMArea { x: 0; y: 0; From 9a80b17e0b84ab4cfb9f41a0fec60fa560a177c5 Mon Sep 17 00:00:00 2001 From: jaby Date: Sun, 26 Jan 2025 17:27:47 +0000 Subject: [PATCH 05/32] Make VRAM seperation work --- src/Tools/tim_tool/ui/app-window.slint | 63 +++++++++++++++++--------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint index 2a4cfb17..4e65623d 100644 --- a/src/Tools/tim_tool/ui/app-window.slint +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -1,30 +1,41 @@ component VRAMSegment inherits Rectangle { - in property area_overlap_width; - in property area_overlap_height; + in property img; + in property clip_x; + in property clip_y; width: 64px; height: 256px; - background: #37f560; - - Rectangle { - x: 0px; - y: 0px; - width: area_overlap_width > 64 ? 64px : area_overlap_width*1px; - height: area_overlap_height > 256 ? 256px : area_overlap_height*1px; - background: #d7e609; + clip: true; + Image { + source: img; + x: -root.clip_x*1px; + y: -root.clip_y*1px; } } component VRAMArea inherits Rectangle { - in property display_area_width; - in property display_area_height; + in property img; + in property img_x; + in property img_y; - for idx in 16 : VRAMSegment { - x: mod(idx,8)*(64+1)*1px; - y: floor(idx/8)*(256+1)*1px; + width: (64*16+15)*1px; + height: (256*2+1)*1px; - area_overlap_width: parent.display_area_width - (mod(idx,8)*64); - area_overlap_height: parent.display_area_height - (256*floor(idx/8)); + for idx in 32 : VRAMSegment { + x: root.get_x(idx)*(64px + 1px); + y: root.get_y(idx)*(256px + 1px); + + img: img; + clip_x: (root.get_x(idx)*64) - root.img_x; + clip_y: (root.get_y(idx)*256) - root.img_y; + } + + function get_x(idx: int) -> int { + return mod(idx, 16); + } + + function get_y(idx: int) -> int { + return floor(idx/16); } } @@ -33,9 +44,19 @@ export component MainWindow inherits Window { min-height: 640px; VRAMArea { - x: 0; - y: 0; - display_area_width: 32+64+3; - display_area_height: 256+128; + x: 0px; + y: 0px; + img: @image-url("../../../../examples/PoolBox/assets/AllTheJaby.png"); + img_x: 0; + img_y: 0; + background: #0365f796; + } + VRAMArea { + x: 0px; + y: 0px; + img: @image-url("../../../../examples/PoolBox/assets/IMG_6921.png"); + img_x: 80; + img_y: 80; + background: #ff000076; } } \ No newline at end of file From 57a76bbe2781ff36cb4f5c2be30d16769f6c51bc Mon Sep 17 00:00:00 2001 From: jaby Date: Sun, 26 Jan 2025 22:46:41 +0000 Subject: [PATCH 06/32] More progress --- src/Tools/tim_tool/ui/app-window.slint | 78 ++++++--------------- src/Tools/tim_tool/ui/file-tab.slint | 3 + src/Tools/tim_tool/ui/main-tab.slint | 36 ++++++++++ src/Tools/tim_tool/ui/vram-components.slint | 40 +++++++++++ 4 files changed, 100 insertions(+), 57 deletions(-) create mode 100644 src/Tools/tim_tool/ui/file-tab.slint create mode 100644 src/Tools/tim_tool/ui/main-tab.slint create mode 100644 src/Tools/tim_tool/ui/vram-components.slint diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint index 4e65623d..8e0d2547 100644 --- a/src/Tools/tim_tool/ui/app-window.slint +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -1,62 +1,26 @@ -component VRAMSegment inherits Rectangle { - in property img; - in property clip_x; - in property clip_y; - - width: 64px; - height: 256px; - clip: true; - Image { - source: img; - x: -root.clip_x*1px; - y: -root.clip_y*1px; - } -} - -component VRAMArea inherits Rectangle { - in property img; - in property img_x; - in property img_y; - - width: (64*16+15)*1px; - height: (256*2+1)*1px; - - for idx in 32 : VRAMSegment { - x: root.get_x(idx)*(64px + 1px); - y: root.get_y(idx)*(256px + 1px); - - img: img; - clip_x: (root.get_x(idx)*64) - root.img_x; - clip_y: (root.get_y(idx)*256) - root.img_y; - } - - function get_x(idx: int) -> int { - return mod(idx, 16); - } - - function get_y(idx: int) -> int { - return floor(idx/16); - } -} +import { MainTab } from "./main-tab.slint"; +import { FileTab } from "./file-tab.slint"; +import { TabWidget } from "std-widgets.slint"; export component MainWindow inherits Window { - min-width: 1024px; - min-height: 640px; + min-width: vram_tab.width; + min-height: vram_tab.height; - VRAMArea { - x: 0px; - y: 0px; - img: @image-url("../../../../examples/PoolBox/assets/AllTheJaby.png"); - img_x: 0; - img_y: 0; - background: #0365f796; - } - VRAMArea { - x: 0px; - y: 0px; - img: @image-url("../../../../examples/PoolBox/assets/IMG_6921.png"); - img_x: 80; - img_y: 80; - background: #ff000076; + TabWidget { + current-index: 1; + file_tab := Tab { + title: "File Bah" + root.min-width/1px; + FileTab { + x: 0px; + y: 0px; + } + } + vram_tab := Tab { + title: "VRAM Layout"; + MainTab { + x: 0px; + y: 0px; + } + } } } \ No newline at end of file diff --git a/src/Tools/tim_tool/ui/file-tab.slint b/src/Tools/tim_tool/ui/file-tab.slint new file mode 100644 index 00000000..50f51cfa --- /dev/null +++ b/src/Tools/tim_tool/ui/file-tab.slint @@ -0,0 +1,3 @@ +export component FileTab inherits Rectangle { + +} \ No newline at end of file diff --git a/src/Tools/tim_tool/ui/main-tab.slint b/src/Tools/tim_tool/ui/main-tab.slint new file mode 100644 index 00000000..541aed68 --- /dev/null +++ b/src/Tools/tim_tool/ui/main-tab.slint @@ -0,0 +1,36 @@ +import { VRAMArea } from "./vram-components.slint"; +import { GroupBox } from "std-widgets.slint"; + +export component MainTab inherits Rectangle { + width: group.width; + height: group.height; + group := GroupBox { + title: "VRAM Layout"; + x: 4px; + y: 4px; + + VerticalLayout { + Rectangle { + width: background_image.width + 8px; + height: background_image.height + 8px; + border-width: 4px; + border-color: black; + background_image := VRAMArea { + x: 4px; + y: 4px; + img: @image-url("../../../../examples/PoolBox/assets/AllTheJaby.png"); + img_x: 0; + img_y: 0; + } + // Extend these from input some how + VRAMArea { + x: 4px; + y: 4px; + img: @image-url("../../../../examples/PoolBox/assets/IMG_6921.png"); + img_x: 80; + img_y: 80; + } + } + } + } +} \ No newline at end of file diff --git a/src/Tools/tim_tool/ui/vram-components.slint b/src/Tools/tim_tool/ui/vram-components.slint new file mode 100644 index 00000000..216a91de --- /dev/null +++ b/src/Tools/tim_tool/ui/vram-components.slint @@ -0,0 +1,40 @@ +component VRAMSegment inherits Rectangle { + in property img; + in property clip_x; + in property clip_y; + + width: 64px; + height: 256px; + clip: true; + Image { + source: img; + x: -root.clip_x*1px; + y: -root.clip_y*1px; + } +} + +export component VRAMArea inherits Rectangle { + in property img; + in property img_x; + in property img_y; + + width: (64*16+15)*1px; + height: (256*2+1)*1px; + + for idx in 32 : VRAMSegment { + x: root.get_x(idx)*(64px + 1px); + y: root.get_y(idx)*(256px + 1px); + + img: img; + clip_x: (root.get_x(idx)*64) - root.img_x; + clip_y: (root.get_y(idx)*256) - root.img_y; + } + + function get_x(idx: int) -> int { + return mod(idx, 16); + } + + function get_y(idx: int) -> int { + return floor(idx/16); + } +} \ No newline at end of file From 09bb3bed374b2affe81f7af71d451526f87671a3 Mon Sep 17 00:00:00 2001 From: jaby Date: Mon, 27 Jan 2025 20:14:02 +0000 Subject: [PATCH 07/32] A solution for the size issues --- src/Tools/tim_tool/ui/app-window.slint | 19 ++++++++++++------- src/Tools/tim_tool/ui/main-tab.slint | 13 +++++++------ 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint index 8e0d2547..de85ffaf 100644 --- a/src/Tools/tim_tool/ui/app-window.slint +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -3,21 +3,26 @@ import { FileTab } from "./file-tab.slint"; import { TabWidget } from "std-widgets.slint"; export component MainWindow inherits Window { - min-width: vram_tab.width; - min-height: vram_tab.height; + min-width: tab_widget.width; + min-height: tab_widget.height; + + tab_widget := TabWidget { + x: 0px; + y: 0px; + width: tab.width; + height: tab.height; - TabWidget { current-index: 1; - file_tab := Tab { - title: "File Bah" + root.min-width/1px; + Tab { + title: "File Bah"; FileTab { x: 0px; y: 0px; } } - vram_tab := Tab { + test := Tab { title: "VRAM Layout"; - MainTab { + tab := MainTab { x: 0px; y: 0px; } diff --git a/src/Tools/tim_tool/ui/main-tab.slint b/src/Tools/tim_tool/ui/main-tab.slint index 541aed68..63eabee1 100644 --- a/src/Tools/tim_tool/ui/main-tab.slint +++ b/src/Tools/tim_tool/ui/main-tab.slint @@ -2,15 +2,16 @@ import { VRAMArea } from "./vram-components.slint"; import { GroupBox } from "std-widgets.slint"; export component MainTab inherits Rectangle { - width: group.width; - height: group.height; + width: group.width + group.x*2; + height: group.height + group.y*2 + 32px; + group := GroupBox { title: "VRAM Layout"; x: 4px; y: 4px; - + VerticalLayout { - Rectangle { + background_rect := Rectangle { width: background_image.width + 8px; height: background_image.height + 8px; border-width: 4px; @@ -23,13 +24,13 @@ export component MainTab inherits Rectangle { img_y: 0; } // Extend these from input some how - VRAMArea { + /*VRAMArea { x: 4px; y: 4px; img: @image-url("../../../../examples/PoolBox/assets/IMG_6921.png"); img_x: 80; img_y: 80; - } + }*/ } } } From 112e61e92dda4f3a848a0a831898341ab16552d5 Mon Sep 17 00:00:00 2001 From: jaby Date: Mon, 27 Jan 2025 20:57:23 +0000 Subject: [PATCH 08/32] Support about tab --- src/Tools/tim_tool/ui/about-tab.slint | 10 ++++++++++ src/Tools/tim_tool/ui/app-window.slint | 13 +++++++++---- src/Tools/tim_tool/ui/main-tab.slint | 18 +++++++++++++++++- src/Tools/tim_tool/ui/vram-components.slint | 1 + 4 files changed, 37 insertions(+), 5 deletions(-) create mode 100644 src/Tools/tim_tool/ui/about-tab.slint diff --git a/src/Tools/tim_tool/ui/about-tab.slint b/src/Tools/tim_tool/ui/about-tab.slint new file mode 100644 index 00000000..039aeb1a --- /dev/null +++ b/src/Tools/tim_tool/ui/about-tab.slint @@ -0,0 +1,10 @@ +import { AboutSlint } from "std-widgets.slint"; +export component AboutTab { + VerticalLayout { + AboutSlint {} + Text { + x: 8pt; + text: "TIM_Tool Version 0.1.0"; + } + } +} \ No newline at end of file diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint index de85ffaf..892e3803 100644 --- a/src/Tools/tim_tool/ui/app-window.slint +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -1,5 +1,6 @@ -import { MainTab } from "./main-tab.slint"; -import { FileTab } from "./file-tab.slint"; +import { AboutTab } from "./about-tab.slint"; +import { FileTab } from "./file-tab.slint"; +import { MainTab } from "./main-tab.slint"; import { TabWidget } from "std-widgets.slint"; export component MainWindow inherits Window { @@ -14,18 +15,22 @@ export component MainWindow inherits Window { current-index: 1; Tab { - title: "File Bah"; + title: "File"; FileTab { x: 0px; y: 0px; } } - test := Tab { + Tab { title: "VRAM Layout"; tab := MainTab { x: 0px; y: 0px; } } + Tab { + title: "About"; + AboutTab {} + } } } \ No newline at end of file diff --git a/src/Tools/tim_tool/ui/main-tab.slint b/src/Tools/tim_tool/ui/main-tab.slint index 63eabee1..07fc348f 100644 --- a/src/Tools/tim_tool/ui/main-tab.slint +++ b/src/Tools/tim_tool/ui/main-tab.slint @@ -1,5 +1,5 @@ import { VRAMArea } from "./vram-components.slint"; -import { GroupBox } from "std-widgets.slint"; +import { GroupBox, StandardListView } from "std-widgets.slint"; export component MainTab inherits Rectangle { width: group.width + group.x*2; @@ -32,6 +32,22 @@ export component MainTab inherits Rectangle { img_y: 80; }*/ } + HorizontalLayout { + GroupBox { + title: "Added Files"; + StandardListView { + width: background_image.width/2; + height: 128px; + model: [ { text: "Blue"}, { text: "Red" }, { text: "Green" }, + { text: "Yellow" }, { text: "Black"}, { text: "White"}, + { text: "Magenta" }, { text: "Cyan" }, + ]; + } + } + GroupBox { + title: "Current File"; + } + } } } } \ No newline at end of file diff --git a/src/Tools/tim_tool/ui/vram-components.slint b/src/Tools/tim_tool/ui/vram-components.slint index 216a91de..b7b869df 100644 --- a/src/Tools/tim_tool/ui/vram-components.slint +++ b/src/Tools/tim_tool/ui/vram-components.slint @@ -1,3 +1,4 @@ +// TODO: Maybe make them inherit Windows...? component VRAMSegment inherits Rectangle { in property img; in property clip_x; From 3c67b2ed4b3f8973efa488d156ad63a549bad87e Mon Sep 17 00:00:00 2001 From: jaby Date: Mon, 27 Jan 2025 20:59:26 +0000 Subject: [PATCH 09/32] Move tabs to tab folder --- src/Tools/tim_tool/ui/app-window.slint | 6 +++--- src/Tools/tim_tool/ui/{ => tab}/about-tab.slint | 0 src/Tools/tim_tool/ui/{ => tab}/file-tab.slint | 0 src/Tools/tim_tool/ui/{ => tab}/main-tab.slint | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) rename src/Tools/tim_tool/ui/{ => tab}/about-tab.slint (100%) rename src/Tools/tim_tool/ui/{ => tab}/file-tab.slint (100%) rename src/Tools/tim_tool/ui/{ => tab}/main-tab.slint (91%) diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint index 892e3803..1a32aa15 100644 --- a/src/Tools/tim_tool/ui/app-window.slint +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -1,6 +1,6 @@ -import { AboutTab } from "./about-tab.slint"; -import { FileTab } from "./file-tab.slint"; -import { MainTab } from "./main-tab.slint"; +import { AboutTab } from "./tab/about-tab.slint"; +import { FileTab } from "./tab/file-tab.slint"; +import { MainTab } from "./tab/main-tab.slint"; import { TabWidget } from "std-widgets.slint"; export component MainWindow inherits Window { diff --git a/src/Tools/tim_tool/ui/about-tab.slint b/src/Tools/tim_tool/ui/tab/about-tab.slint similarity index 100% rename from src/Tools/tim_tool/ui/about-tab.slint rename to src/Tools/tim_tool/ui/tab/about-tab.slint diff --git a/src/Tools/tim_tool/ui/file-tab.slint b/src/Tools/tim_tool/ui/tab/file-tab.slint similarity index 100% rename from src/Tools/tim_tool/ui/file-tab.slint rename to src/Tools/tim_tool/ui/tab/file-tab.slint diff --git a/src/Tools/tim_tool/ui/main-tab.slint b/src/Tools/tim_tool/ui/tab/main-tab.slint similarity index 91% rename from src/Tools/tim_tool/ui/main-tab.slint rename to src/Tools/tim_tool/ui/tab/main-tab.slint index 07fc348f..a3d9b02b 100644 --- a/src/Tools/tim_tool/ui/main-tab.slint +++ b/src/Tools/tim_tool/ui/tab/main-tab.slint @@ -1,4 +1,4 @@ -import { VRAMArea } from "./vram-components.slint"; +import { VRAMArea } from "../vram-components.slint"; import { GroupBox, StandardListView } from "std-widgets.slint"; export component MainTab inherits Rectangle { @@ -19,7 +19,7 @@ export component MainTab inherits Rectangle { background_image := VRAMArea { x: 4px; y: 4px; - img: @image-url("../../../../examples/PoolBox/assets/AllTheJaby.png"); + img: @image-url("../../../../../examples/PoolBox/assets/AllTheJaby.png"); img_x: 0; img_y: 0; } From 89229c6e3b2eef5980472cd2e15b9f1d2bc29c8d Mon Sep 17 00:00:00 2001 From: jaby Date: Mon, 27 Jan 2025 21:54:21 +0000 Subject: [PATCH 10/32] Finalize it GUI --- src/Tools/tim_tool/ui/app-window.slint | 5 ++- src/Tools/tim_tool/ui/tab/about-tab.slint | 22 ++++++++++- src/Tools/tim_tool/ui/tab/main-tab.slint | 45 ++++++++++++++++++----- 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint index 1a32aa15..aee785d0 100644 --- a/src/Tools/tim_tool/ui/app-window.slint +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -4,8 +4,9 @@ import { MainTab } from "./tab/main-tab.slint"; import { TabWidget } from "std-widgets.slint"; export component MainWindow inherits Window { - min-width: tab_widget.width; - min-height: tab_widget.height; + title: "TIM Tool 0.1.0"; + width: tab_widget.width; + height: tab_widget.height; tab_widget := TabWidget { x: 0px; diff --git a/src/Tools/tim_tool/ui/tab/about-tab.slint b/src/Tools/tim_tool/ui/tab/about-tab.slint index 039aeb1a..6f470cf6 100644 --- a/src/Tools/tim_tool/ui/tab/about-tab.slint +++ b/src/Tools/tim_tool/ui/tab/about-tab.slint @@ -1,10 +1,28 @@ import { AboutSlint } from "std-widgets.slint"; export component AboutTab { + y: 0px; VerticalLayout { - AboutSlint {} + padding: 8px; + alignment: start; Text { - x: 8pt; + font-size: 24pt; text: "TIM_Tool Version 0.1.0"; + horizontal-alignment: center; } + Text { + font-size: 20pt; + text: "Part of JabyEngine"; + horizontal-alignment: center; + } + Text { + font-size: 16pt; + text: "MIT License"; + horizontal-alignment: center; + } + Text { + font-size: 16pt; + text: " "; + } + AboutSlint {} } } \ No newline at end of file diff --git a/src/Tools/tim_tool/ui/tab/main-tab.slint b/src/Tools/tim_tool/ui/tab/main-tab.slint index a3d9b02b..ed9864b2 100644 --- a/src/Tools/tim_tool/ui/tab/main-tab.slint +++ b/src/Tools/tim_tool/ui/tab/main-tab.slint @@ -1,5 +1,5 @@ import { VRAMArea } from "../vram-components.slint"; -import { GroupBox, StandardListView } from "std-widgets.slint"; +import { Button, ComboBox, GroupBox, StandardListView } from "std-widgets.slint"; export component MainTab inherits Rectangle { width: group.width + group.x*2; @@ -15,7 +15,8 @@ export component MainTab inherits Rectangle { width: background_image.width + 8px; height: background_image.height + 8px; border-width: 4px; - border-color: black; + border-color: #404040; + background: #A0A0A0; background_image := VRAMArea { x: 4px; y: 4px; @@ -33,19 +34,45 @@ export component MainTab inherits Rectangle { }*/ } HorizontalLayout { + padding: 4px; GroupBox { title: "Added Files"; - StandardListView { - width: background_image.width/2; - height: 128px; - model: [ { text: "Blue"}, { text: "Red" }, { text: "Green" }, - { text: "Yellow" }, { text: "Black"}, { text: "White"}, - { text: "Magenta" }, { text: "Cyan" }, - ]; + VerticalLayout { + alignment: start; + padding: 4px; + StandardListView { + width: background_image.width/2; + height: 128px; + model: [ { text: "Blue"}, { text: "Red" }, { text: "Green" }, + { text: "Yellow" }, { text: "Black"}, { text: "White"}, + { text: "Magenta" }, { text: "Cyan" }, + ]; + } + HorizontalLayout { + padding: 4px; + Button { + text: "Add File"; + } + Button { + text: "Remove File"; + } + } } } GroupBox { title: "Current File"; + VerticalLayout { + padding: 4px; + Rectangle { + width: 128px; + height: 128px; + background: #A0A0A0; + } + ComboBox { + model: ["4-bit", "16-bit", "24-bit"]; + current-value: "4-bit"; + } + } } } } From f7ae70b901ed0e67e1a4edec533946ddaea3429d Mon Sep 17 00:00:00 2001 From: jaby Date: Sun, 2 Feb 2025 21:12:37 +0000 Subject: [PATCH 11/32] Remove standard elements --- src/Tools/tim_tool/ui/tab/main-tab.slint | 45 +++++++++++++++--------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/Tools/tim_tool/ui/tab/main-tab.slint b/src/Tools/tim_tool/ui/tab/main-tab.slint index ed9864b2..33367df1 100644 --- a/src/Tools/tim_tool/ui/tab/main-tab.slint +++ b/src/Tools/tim_tool/ui/tab/main-tab.slint @@ -1,7 +1,16 @@ import { VRAMArea } from "../vram-components.slint"; import { Button, ComboBox, GroupBox, StandardListView } from "std-widgets.slint"; +struct VRAMImage { + img: image, + x: int, + y: int, +} + export component MainTab inherits Rectangle { + in property <[StandardListViewItem]> vram_file_list: []; + in property <[VRAMImage]> vram_images: []; + width: group.width + group.x*2; height: group.height + group.y*2 + 32px; @@ -12,26 +21,26 @@ export component MainTab inherits Rectangle { VerticalLayout { background_rect := Rectangle { - width: background_image.width + 8px; - height: background_image.height + 8px; - border-width: 4px; + width: background_image.width + root.get_border_width()*2px; + height: background_image.height + root.get_border_width()*2px; + border-width: root.get_border_width()*1px; border-color: #404040; background: #A0A0A0; background_image := VRAMArea { - x: 4px; - y: 4px; + x: root.get_border_width()*1px; + y: root.get_border_width()*1px; img: @image-url("../../../../../examples/PoolBox/assets/AllTheJaby.png"); img_x: 0; img_y: 0; } - // Extend these from input some how - /*VRAMArea { - x: 4px; - y: 4px; - img: @image-url("../../../../examples/PoolBox/assets/IMG_6921.png"); - img_x: 80; - img_y: 80; - }*/ + + for vram_image[i] in root.vram_images: VRAMArea { + x: root.get_border_width()*1px; + y: root.get_border_width()*1px; + img: vram-image.img; + img_x: vram-image.x; + img_y: vram-image.y; + } } HorizontalLayout { padding: 4px; @@ -43,10 +52,8 @@ export component MainTab inherits Rectangle { StandardListView { width: background_image.width/2; height: 128px; - model: [ { text: "Blue"}, { text: "Red" }, { text: "Green" }, - { text: "Yellow" }, { text: "Black"}, { text: "White"}, - { text: "Magenta" }, { text: "Cyan" }, - ]; + + model: root.vram_file_list; } HorizontalLayout { padding: 4px; @@ -77,4 +84,8 @@ export component MainTab inherits Rectangle { } } } + + function get_border_width() -> int { + return 4; + } } \ No newline at end of file From a9fd19467251f986f65d6e846eeb16fba25991da Mon Sep 17 00:00:00 2001 From: jaby Date: Mon, 3 Feb 2025 21:00:12 +0000 Subject: [PATCH 12/32] Connect add and remove buttons --- src/Tools/tim_tool/src/main.rs | 40 ++++++++++++++++++++++-- src/Tools/tim_tool/ui/app-window.slint | 5 +++ src/Tools/tim_tool/ui/tab/main-tab.slint | 19 +++++++---- 3 files changed, 56 insertions(+), 8 deletions(-) diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index bf454cd4..fb352245 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -1,10 +1,46 @@ // Prevent console window in addition to Slint window in Windows release builds when, e.g., starting the app via file manager. Ignored on other platforms. #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +use slint::Model; +use std::{cell::RefCell, rc::Rc}; + slint::include_modules!(); -fn main() -> Result<(), slint::PlatformError> { - let main_window = MainWindow::new()?; +struct GUIElements { + tab_vram_file_list: Rc> +} +impl GUIElements { + pub fn new(main_window: &MainWindow) -> GUIElements { + let tab_vram_file_list:Vec = main_window.get_main_tab_vram_file_list().iter().collect(); + let tab_vram_file_list = Rc::new(slint::VecModel::from(tab_vram_file_list)); + + main_window.set_main_tab_vram_file_list(tab_vram_file_list.clone().into()); + GUIElements{tab_vram_file_list} + } + + pub fn add_new_vram_file(&mut self, file: &str) { + self.tab_vram_file_list.push(slint::StandardListViewItem::from(file)); + } + + pub fn remove_vram_file(&mut self, idx: usize) { + self.tab_vram_file_list.remove(idx); + } +} + +fn main() -> Result<(), slint::PlatformError> { + let main_window = MainWindow::new()?; + let gui_elements = Rc::new(RefCell::new(GUIElements::new(&main_window))); + + let gui_elements_cloned = gui_elements.clone(); + main_window.on_main_tab_add_file_clicked(move || { + gui_elements_cloned.borrow_mut().add_new_vram_file("Planschi"); + }); + let gui_elements_cloned = gui_elements.clone(); + main_window.on_main_tab_remove_file_clicked(move |idx: i32| { + if idx >= 0 { + gui_elements_cloned.borrow_mut().remove_vram_file(idx as usize); + } + }); main_window.run() } \ No newline at end of file diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint index aee785d0..060a7444 100644 --- a/src/Tools/tim_tool/ui/app-window.slint +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -4,6 +4,11 @@ import { MainTab } from "./tab/main-tab.slint"; import { TabWidget } from "std-widgets.slint"; export component MainWindow inherits Window { + in-out property main_tab_vram_file_list <=> tab.vram_files; + + callback main_tab_add_file_clicked <=> tab.add_file_clicked; + callback main_tab_remove_file_clicked <=> tab.remove_file_clicked; + title: "TIM Tool 0.1.0"; width: tab_widget.width; height: tab_widget.height; diff --git a/src/Tools/tim_tool/ui/tab/main-tab.slint b/src/Tools/tim_tool/ui/tab/main-tab.slint index 33367df1..76a1c739 100644 --- a/src/Tools/tim_tool/ui/tab/main-tab.slint +++ b/src/Tools/tim_tool/ui/tab/main-tab.slint @@ -8,8 +8,11 @@ struct VRAMImage { } export component MainTab inherits Rectangle { - in property <[StandardListViewItem]> vram_file_list: []; - in property <[VRAMImage]> vram_images: []; + in-out property <[StandardListViewItem]> vram_files: []; + in-out property <[VRAMImage]> vram_images: []; + + callback add_file_clicked(); + callback remove_file_clicked(int); width: group.width + group.x*2; height: group.height + group.y*2 + 32px; @@ -49,19 +52,23 @@ export component MainTab inherits Rectangle { VerticalLayout { alignment: start; padding: 4px; - StandardListView { - width: background_image.width/2; + vram_files_list := StandardListView { + width: background_image.width/2; height: 128px; - - model: root.vram_file_list; + model: root.vram_files; } HorizontalLayout { padding: 4px; Button { text: "Add File"; + clicked => {root.add_file_clicked();} } Button { text: "Remove File"; + clicked => { + root.remove_file_clicked(vram_files_list.current_item); + vram_files_list.current-item = -1; + } } } } From 2993fa661ac96f6804e3dd4866b6cb22ea4ee80a Mon Sep 17 00:00:00 2001 From: jaby Date: Mon, 3 Feb 2025 22:35:15 +0000 Subject: [PATCH 13/32] Get moving tiles sorted --- src/Tools/tim_tool/src/main.rs | 59 ++++++++++++++++++++---- src/Tools/tim_tool/ui/app-window.slint | 2 + src/Tools/tim_tool/ui/tab/main-tab.slint | 43 +++++++++++++++-- 3 files changed, 92 insertions(+), 12 deletions(-) diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index fb352245..f4c192b3 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -2,29 +2,67 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] use slint::Model; -use std::{cell::RefCell, rc::Rc}; +use std::{cell::RefCell, path::Path, rc::Rc}; slint::include_modules!(); struct GUIElements { - tab_vram_file_list: Rc> + vram_file_list: Rc>, + vram_image_list: Rc> } impl GUIElements { pub fn new(main_window: &MainWindow) -> GUIElements { - let tab_vram_file_list:Vec = main_window.get_main_tab_vram_file_list().iter().collect(); - let tab_vram_file_list = Rc::new(slint::VecModel::from(tab_vram_file_list)); + let vram_file_list:Vec = main_window.get_main_tab_vram_file_list().iter().collect(); + let vram_file_list = Rc::new(slint::VecModel::from(vram_file_list)); - main_window.set_main_tab_vram_file_list(tab_vram_file_list.clone().into()); - GUIElements{tab_vram_file_list} + let vram_image_list = Rc::new(slint::VecModel::from(Vec::::new())); + + main_window.set_main_tab_vram_file_list(vram_file_list.clone().into()); + main_window.set_main_tab_vram_images(vram_image_list.clone().into()); + GUIElements{vram_file_list, vram_image_list} } pub fn add_new_vram_file(&mut self, file: &str) { - self.tab_vram_file_list.push(slint::StandardListViewItem::from(file)); + let vram_image = VRAMImage{ + img: slint::Image::load_from_path(Path::new("/home/jaby/Desktop/PSX_Dev/jabyengine/examples/PoolBox/assets/IMG_6921.png")).unwrap(), + x: 0, + y: 0 + }; + + self.vram_file_list.push(slint::StandardListViewItem::from(file)); + self.vram_image_list.push(vram_image); } pub fn remove_vram_file(&mut self, idx: usize) { - self.tab_vram_file_list.remove(idx); + self.vram_file_list.remove(idx); + self.vram_image_list.remove(idx); + } + + pub fn move_vram_image(&mut self, idx: usize, dx: i32, dy: i32) { + if let Some(mut vram_info) = self.vram_image_list.row_data(idx) { + vram_info.x += dx; + vram_info.y += dy; + + if vram_info.x < 0 { + vram_info.x = 0; + } + + if vram_info.y < 0 { + vram_info.y = 0; + } + + let (vram_img_width, vram_img_height) = (vram_info.img.size().width as i32, vram_info.img.size().height as i32); + if (vram_info.x + vram_img_width) > 1024 { + vram_info.x = 1024 - vram_img_width; + } + + if (vram_info.y + vram_img_width) > 512 { + vram_info.y = 512 - vram_img_height; + } + + self.vram_image_list.set_row_data(idx, vram_info); + } } } @@ -42,5 +80,10 @@ fn main() -> Result<(), slint::PlatformError> { gui_elements_cloned.borrow_mut().remove_vram_file(idx as usize); } }); + let gui_elements_cloned = gui_elements.clone(); + main_window.on_move_vram_image(move |idx: i32, dx: i32, dy: i32| { + gui_elements_cloned.borrow_mut().move_vram_image(idx as usize, dx, dy); + }); + main_window.run() } \ No newline at end of file diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint index 060a7444..fb165e95 100644 --- a/src/Tools/tim_tool/ui/app-window.slint +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -5,9 +5,11 @@ import { TabWidget } from "std-widgets.slint"; export component MainWindow inherits Window { in-out property main_tab_vram_file_list <=> tab.vram_files; + in-out property main_tab_vram_images <=> tab.vram_images; callback main_tab_add_file_clicked <=> tab.add_file_clicked; callback main_tab_remove_file_clicked <=> tab.remove_file_clicked; + callback move_vram_image <=> tab.move_vram_image; title: "TIM Tool 0.1.0"; width: tab_widget.width; diff --git a/src/Tools/tim_tool/ui/tab/main-tab.slint b/src/Tools/tim_tool/ui/tab/main-tab.slint index 76a1c739..10a44f31 100644 --- a/src/Tools/tim_tool/ui/tab/main-tab.slint +++ b/src/Tools/tim_tool/ui/tab/main-tab.slint @@ -2,9 +2,9 @@ import { VRAMArea } from "../vram-components.slint"; import { Button, ComboBox, GroupBox, StandardListView } from "std-widgets.slint"; struct VRAMImage { - img: image, - x: int, - y: int, + img: image, + x: int, + y: int, } export component MainTab inherits Rectangle { @@ -13,6 +13,7 @@ export component MainTab inherits Rectangle { callback add_file_clicked(); callback remove_file_clicked(int); + callback move_vram_image(int, int, int); width: group.width + group.x*2; height: group.height + group.y*2 + 32px; @@ -40,9 +41,43 @@ export component MainTab inherits Rectangle { for vram_image[i] in root.vram_images: VRAMArea { x: root.get_border_width()*1px; y: root.get_border_width()*1px; - img: vram-image.img; + img: vram-image.img; img_x: vram-image.x; img_y: vram-image.y; + TouchArea { + x: (parent.img_x + self.lines_crossed_x())*1px; + y: (parent.img_y + self.lines_crossed_y())*1px; + width: (parent.img.width + self.lines_crossed_width())*1px; + height: (parent.img.height + self.lines_crossed_height())*1px; + mouse-cursor: grab; + moved => { + self.mouse-cursor = MouseCursor.grabbing; + root.move_vram_image(i, (self.mouse-x - self.pressed-x)/1px, (self.mouse-y - self.pressed-y)/1px); + + } + pointer-event(event) => { + if event.kind == PointerEventKind.up { + self.mouse-cursor = MouseCursor.grab; + } + } + + // Thanks to Cody the white tiger + function lines_crossed_x() -> int { + return parent.img_x/64; + } + + function lines_crossed_y() -> int { + return parent.img_y/256; + } + + function lines_crossed_width() -> int { + return ((parent.img_x + parent.img.width)/64) - self.lines_crossed_x(); + } + + function lines_crossed_height() -> int { + return ((parent.img_y + parent.img.height)/256) - self.lines_crossed_y(); + } + } } } HorizontalLayout { From 3e0fb8396d863d2ed9dc82f44a0095c6f739176b Mon Sep 17 00:00:00 2001 From: Jaby Date: Sun, 9 Feb 2025 17:00:24 +0100 Subject: [PATCH 14/32] Support proper background and coordinates now --- src/Tools/tim_tool/Cargo.toml | 3 ++- src/Tools/tim_tool/src/gui/mod.rs | 28 ++++++++++++++++++++++++ src/Tools/tim_tool/src/main.rs | 7 ++++++ src/Tools/tim_tool/ui/app-window.slint | 18 ++++++++------- src/Tools/tim_tool/ui/tab/main-tab.slint | 27 ++++++++++++++++++----- 5 files changed, 68 insertions(+), 15 deletions(-) create mode 100644 src/Tools/tim_tool/src/gui/mod.rs diff --git a/src/Tools/tim_tool/Cargo.toml b/src/Tools/tim_tool/Cargo.toml index c695b90c..c2eff597 100644 --- a/src/Tools/tim_tool/Cargo.toml +++ b/src/Tools/tim_tool/Cargo.toml @@ -7,7 +7,8 @@ edition = "2021" panic = "abort" [dependencies] -slint = "1.9.2" +tiny-skia = "0.11.4" +slint = "1.9.2" [build-dependencies] slint-build = "1.9.2" diff --git a/src/Tools/tim_tool/src/gui/mod.rs b/src/Tools/tim_tool/src/gui/mod.rs new file mode 100644 index 00000000..ecf31581 --- /dev/null +++ b/src/Tools/tim_tool/src/gui/mod.rs @@ -0,0 +1,28 @@ +use slint::{Rgba8Pixel, SharedPixelBuffer}; +use tiny_skia::{Rect, Transform}; + +pub fn create_vram_bg(rect_width: u32, rect_height: u32) -> Option> { + let mut pixel_buffer = SharedPixelBuffer::::new(1024, 512); + let width = pixel_buffer.width(); + let height = pixel_buffer.height(); + + if let Some(mut pixmap) = tiny_skia::PixmapMut::from_bytes(pixel_buffer.make_mut_bytes(), width, height) { + let vram_color = tiny_skia::Color::from_rgba8(0x0, 0x80, 0x80, 0xFF); + let display_buffer_color = tiny_skia::Color::from_rgba8(0x0, 0x80, 0x0, 0xFF); + let draw_area_color = tiny_skia::Color::from_rgba8(0x80, 0x80, 0x0, 0xFF); + + let mut paint = tiny_skia::Paint::default(); + + pixmap.fill(vram_color); + + paint.set_color(display_buffer_color); + pixmap.fill_rect(Rect::from_xywh(0.0, 0.0, rect_width as f32, rect_height as f32).unwrap(), &paint, Transform::identity(), None); + paint.set_color(draw_area_color); + pixmap.fill_rect(Rect::from_xywh(0.0, rect_height as f32, rect_width as f32, rect_height as f32).unwrap(), &paint, Transform::identity(), None); + Some(pixel_buffer) + } + + else { + None + } +} \ No newline at end of file diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index f4c192b3..6793aeed 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -1,6 +1,7 @@ // Prevent console window in addition to Slint window in Windows release builds when, e.g., starting the app via file manager. Ignored on other platforms. #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] +mod gui; use slint::Model; use std::{cell::RefCell, path::Path, rc::Rc}; @@ -64,12 +65,18 @@ impl GUIElements { self.vram_image_list.set_row_data(idx, vram_info); } } + + pub fn create_bg() -> Result { + Ok(slint::Image::from_rgba8_premultiplied(gui::create_vram_bg(320, 256).ok_or(slint::PlatformError::Other("Failed creating VRAM background image".to_string()))?)) + } } fn main() -> Result<(), slint::PlatformError> { let main_window = MainWindow::new()?; let gui_elements = Rc::new(RefCell::new(GUIElements::new(&main_window))); + main_window.set_main_tab_vram_bg(GUIElements::create_bg()?); + let gui_elements_cloned = gui_elements.clone(); main_window.on_main_tab_add_file_clicked(move || { gui_elements_cloned.borrow_mut().add_new_vram_file("Planschi"); diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint index fb165e95..88453d12 100644 --- a/src/Tools/tim_tool/ui/app-window.slint +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -4,12 +4,14 @@ import { MainTab } from "./tab/main-tab.slint"; import { TabWidget } from "std-widgets.slint"; export component MainWindow inherits Window { - in-out property main_tab_vram_file_list <=> tab.vram_files; - in-out property main_tab_vram_images <=> tab.vram_images; + // Main Tab values + in-out property main_tab_vram_bg <=> main_tab.vram_bg; + in-out property main_tab_vram_file_list <=> main_tab.vram_files; + in-out property main_tab_vram_images <=> main_tab.vram_images; - callback main_tab_add_file_clicked <=> tab.add_file_clicked; - callback main_tab_remove_file_clicked <=> tab.remove_file_clicked; - callback move_vram_image <=> tab.move_vram_image; + callback main_tab_add_file_clicked <=> main_tab.add_file_clicked; + callback main_tab_remove_file_clicked <=> main_tab.remove_file_clicked; + callback move_vram_image <=> main_tab.move_vram_image; title: "TIM Tool 0.1.0"; width: tab_widget.width; @@ -18,8 +20,8 @@ export component MainWindow inherits Window { tab_widget := TabWidget { x: 0px; y: 0px; - width: tab.width; - height: tab.height; + width: main_tab.width; + height: main_tab.height; current-index: 1; Tab { @@ -31,7 +33,7 @@ export component MainWindow inherits Window { } Tab { title: "VRAM Layout"; - tab := MainTab { + main_tab := MainTab { x: 0px; y: 0px; } diff --git a/src/Tools/tim_tool/ui/tab/main-tab.slint b/src/Tools/tim_tool/ui/tab/main-tab.slint index 10a44f31..b816924c 100644 --- a/src/Tools/tim_tool/ui/tab/main-tab.slint +++ b/src/Tools/tim_tool/ui/tab/main-tab.slint @@ -8,6 +8,7 @@ struct VRAMImage { } export component MainTab inherits Rectangle { + in-out property vram_bg; in-out property <[StandardListViewItem]> vram_files: []; in-out property <[VRAMImage]> vram_images: []; @@ -31,16 +32,16 @@ export component MainTab inherits Rectangle { border-color: #404040; background: #A0A0A0; background_image := VRAMArea { - x: root.get_border_width()*1px; - y: root.get_border_width()*1px; - img: @image-url("../../../../../examples/PoolBox/assets/AllTheJaby.png"); + x: root.get_border_width()*1px; + y: root.get_border_width()*1px; + img: vram_bg; img_x: 0; img_y: 0; } for vram_image[i] in root.vram_images: VRAMArea { - x: root.get_border_width()*1px; - y: root.get_border_width()*1px; + x: root.get_border_width()*1px; + y: root.get_border_width()*1px; img: vram-image.img; img_x: vram-image.x; img_y: vram-image.y; @@ -53,12 +54,18 @@ export component MainTab inherits Rectangle { moved => { self.mouse-cursor = MouseCursor.grabbing; root.move_vram_image(i, (self.mouse-x - self.pressed-x)/1px, (self.mouse-y - self.pressed-y)/1px); - + cur_sel_x.display_value = parent.img_x; + cur_sel_y.display_value = parent.img_y; } pointer-event(event) => { if event.kind == PointerEventKind.up { self.mouse-cursor = MouseCursor.grab; } + + if event.kind == PointerEventKind.down { + cur_sel_x.display_value = parent.img_x; + cur_sel_y.display_value = parent.img_y; + } } // Thanks to Cody the white tiger @@ -117,6 +124,14 @@ export component MainTab inherits Rectangle { height: 128px; background: #A0A0A0; } + cur_sel_x := Text { + in-out property display_value; + text: "X: " + display_value; + } + cur_sel_y :=Text { + in-out property display_value; + text: "Y: " + display_value; + } ComboBox { model: ["4-bit", "16-bit", "24-bit"]; current-value: "4-bit"; From 3009b116754aecff827089d4633563a72b0ade44 Mon Sep 17 00:00:00 2001 From: Jaby Date: Sun, 9 Feb 2025 17:17:39 +0100 Subject: [PATCH 15/32] Support preview image --- src/Tools/tim_tool/ui/tab/main-tab.slint | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Tools/tim_tool/ui/tab/main-tab.slint b/src/Tools/tim_tool/ui/tab/main-tab.slint index b816924c..a91d7326 100644 --- a/src/Tools/tim_tool/ui/tab/main-tab.slint +++ b/src/Tools/tim_tool/ui/tab/main-tab.slint @@ -65,6 +65,7 @@ export component MainTab inherits Rectangle { if event.kind == PointerEventKind.down { cur_sel_x.display_value = parent.img_x; cur_sel_y.display_value = parent.img_y; + cur_sel_img.source = parent.img; } } @@ -123,6 +124,11 @@ export component MainTab inherits Rectangle { width: 128px; height: 128px; background: #A0A0A0; + cur_sel_img := Image { + width: 128px; + height: 128px; + image-fit: contain; + } } cur_sel_x := Text { in-out property display_value; From 7f831bc09b49db307855eaaa9595025ca0f9a320 Mon Sep 17 00:00:00 2001 From: Jaby Date: Sun, 9 Feb 2025 19:31:42 +0100 Subject: [PATCH 16/32] Support loading image --- src/Tools/tim_tool/Cargo.toml | 6 +++-- src/Tools/tim_tool/src/gui/mod.rs | 5 +++- src/Tools/tim_tool/src/main.rs | 38 +++++++++++++++++++++++-------- 3 files changed, 37 insertions(+), 12 deletions(-) diff --git a/src/Tools/tim_tool/Cargo.toml b/src/Tools/tim_tool/Cargo.toml index c2eff597..f07694dc 100644 --- a/src/Tools/tim_tool/Cargo.toml +++ b/src/Tools/tim_tool/Cargo.toml @@ -7,8 +7,10 @@ edition = "2021" panic = "abort" [dependencies] -tiny-skia = "0.11.4" -slint = "1.9.2" +rfd = "0.15.2" +slint = "1.9.2" +tiny-skia = "0.11.4" +tool_helper = {path = "../tool_helper"} [build-dependencies] slint-build = "1.9.2" diff --git a/src/Tools/tim_tool/src/gui/mod.rs b/src/Tools/tim_tool/src/gui/mod.rs index ecf31581..fe1dec0d 100644 --- a/src/Tools/tim_tool/src/gui/mod.rs +++ b/src/Tools/tim_tool/src/gui/mod.rs @@ -1,8 +1,11 @@ use slint::{Rgba8Pixel, SharedPixelBuffer}; use tiny_skia::{Rect, Transform}; +pub const VRAM_WIDTH:usize = 1024; +pub const VRAM_HEIGHT:usize = 512; + pub fn create_vram_bg(rect_width: u32, rect_height: u32) -> Option> { - let mut pixel_buffer = SharedPixelBuffer::::new(1024, 512); + let mut pixel_buffer = SharedPixelBuffer::::new(VRAM_WIDTH as u32, VRAM_HEIGHT as u32); let width = pixel_buffer.width(); let height = pixel_buffer.height(); diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index 6793aeed..3a2723f8 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -3,7 +3,9 @@ mod gui; use slint::Model; -use std::{cell::RefCell, path::Path, rc::Rc}; +use rfd::{FileDialog, MessageDialog}; +use std::{cell::RefCell, ffi::OsStr, path::PathBuf, rc::Rc}; +use tool_helper::Error; slint::include_modules!(); @@ -24,15 +26,22 @@ impl GUIElements { GUIElements{vram_file_list, vram_image_list} } - pub fn add_new_vram_file(&mut self, file: &str) { + pub fn add_new_vram_file(&mut self, file: PathBuf) -> Result<(), Error> { let vram_image = VRAMImage{ - img: slint::Image::load_from_path(Path::new("/home/jaby/Desktop/PSX_Dev/jabyengine/examples/PoolBox/assets/IMG_6921.png")).unwrap(), + img: slint::Image::load_from_path(&file).or_else(|_| {Err(Error::from_str("Failed loading image"))})?, x: 0, y: 0 }; - self.vram_file_list.push(slint::StandardListViewItem::from(file)); + let img_size = vram_image.img.size(); + if img_size.width > gui::VRAM_WIDTH as u32 || img_size.height > gui::VRAM_HEIGHT as u32 { + return Err(Error::from_text(format!("Image size ({}; {}) is to big for VRAM ({}, {})", img_size.width, img_size.height, gui::VRAM_WIDTH, gui::VRAM_HEIGHT))); + } + + let file_name = file.file_name().get_or_insert(OsStr::new("")).to_string_lossy().to_string(); + self.vram_file_list.push(slint::StandardListViewItem::from(file_name.as_str())); self.vram_image_list.push(vram_image); + Ok(()) } pub fn remove_vram_file(&mut self, idx: usize) { @@ -54,12 +63,12 @@ impl GUIElements { } let (vram_img_width, vram_img_height) = (vram_info.img.size().width as i32, vram_info.img.size().height as i32); - if (vram_info.x + vram_img_width) > 1024 { - vram_info.x = 1024 - vram_img_width; + if (vram_info.x + vram_img_width) > gui::VRAM_WIDTH as i32 { + vram_info.x = gui::VRAM_WIDTH as i32 - vram_img_width; } - if (vram_info.y + vram_img_width) > 512 { - vram_info.y = 512 - vram_img_height; + if (vram_info.y + vram_img_height) > gui::VRAM_HEIGHT as i32 { + vram_info.y = gui::VRAM_HEIGHT as i32 - vram_img_height; } self.vram_image_list.set_row_data(idx, vram_info); @@ -79,7 +88,18 @@ fn main() -> Result<(), slint::PlatformError> { let gui_elements_cloned = gui_elements.clone(); main_window.on_main_tab_add_file_clicked(move || { - gui_elements_cloned.borrow_mut().add_new_vram_file("Planschi"); + let file = FileDialog::new() + .add_filter("Images", &["png", "bmp", "jpeg"]) + .add_filter("All", &[""]) + .set_title("Add image") + //.set_directory("/") + .pick_file(); + + if let Some(file) = file { + if let Result::Err(error) = gui_elements_cloned.borrow_mut().add_new_vram_file(file) { + MessageDialog::new().set_title("Loading Image failed").set_level(rfd::MessageLevel::Error).set_description(error.to_string()).show(); + } + } }); let gui_elements_cloned = gui_elements.clone(); main_window.on_main_tab_remove_file_clicked(move |idx: i32| { From 3054fab315a2d8334fa5d2c27dec43c4ba0e76e7 Mon Sep 17 00:00:00 2001 From: Jaby Date: Mon, 10 Feb 2025 22:16:25 +0100 Subject: [PATCH 17/32] Retrieve file conversion path --- src/Tools/tim_tool/src/main.rs | 66 +++++++++++------ src/Tools/tim_tool/ui/app-window.slint | 6 +- src/Tools/tim_tool/ui/tab/file-tab.slint | 93 +++++++++++++++++++++++- src/Tools/tim_tool/ui/tab/main-tab.slint | 6 +- 4 files changed, 145 insertions(+), 26 deletions(-) diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index 3a2723f8..6e14a374 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -2,7 +2,7 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] mod gui; -use slint::Model; +use slint::{Model, SharedString}; use rfd::{FileDialog, MessageDialog}; use std::{cell::RefCell, ffi::OsStr, path::PathBuf, rc::Rc}; use tool_helper::Error; @@ -10,20 +10,22 @@ use tool_helper::Error; slint::include_modules!(); struct GUIElements { - vram_file_list: Rc>, - vram_image_list: Rc> + pub main_window: Rc>, + vram_file_list: Rc>, + vram_image_list: Rc>, } impl GUIElements { - pub fn new(main_window: &MainWindow) -> GUIElements { - let vram_file_list:Vec = main_window.get_main_tab_vram_file_list().iter().collect(); + pub fn new(main_window: Rc>) -> GUIElements { + let vram_file_list:Vec = main_window.borrow().get_main_tab_vram_file_list().iter().collect(); let vram_file_list = Rc::new(slint::VecModel::from(vram_file_list)); let vram_image_list = Rc::new(slint::VecModel::from(Vec::::new())); - main_window.set_main_tab_vram_file_list(vram_file_list.clone().into()); - main_window.set_main_tab_vram_images(vram_image_list.clone().into()); - GUIElements{vram_file_list, vram_image_list} + main_window.borrow().set_main_tab_vram_file_list(vram_file_list.clone().into()); + main_window.borrow().set_main_tab_vram_images(vram_image_list.clone().into()); + + GUIElements{main_window, vram_file_list, vram_image_list} } pub fn add_new_vram_file(&mut self, file: PathBuf) -> Result<(), Error> { @@ -81,35 +83,57 @@ impl GUIElements { } fn main() -> Result<(), slint::PlatformError> { - let main_window = MainWindow::new()?; - let gui_elements = Rc::new(RefCell::new(GUIElements::new(&main_window))); + let main_window = Rc::new(RefCell::new(MainWindow::new()?)); + let gui_elements = Rc::new(RefCell::new(GUIElements::new(main_window.clone()))); + let main_window = main_window.borrow(); main_window.set_main_tab_vram_bg(GUIElements::create_bg()?); let gui_elements_cloned = gui_elements.clone(); main_window.on_main_tab_add_file_clicked(move || { - let file = FileDialog::new() + let mut gui_elements = gui_elements_cloned.borrow_mut(); + let file = FileDialog::new() .add_filter("Images", &["png", "bmp", "jpeg"]) .add_filter("All", &[""]) - .set_title("Add image") - //.set_directory("/") + .set_title("Add TIM image") .pick_file(); if let Some(file) = file { - if let Result::Err(error) = gui_elements_cloned.borrow_mut().add_new_vram_file(file) { + if let Result::Err(error) = gui_elements.add_new_vram_file(file) { MessageDialog::new().set_title("Loading Image failed").set_level(rfd::MessageLevel::Error).set_description(error.to_string()).show(); } - } - }); - let gui_elements_cloned = gui_elements.clone(); - main_window.on_main_tab_remove_file_clicked(move |idx: i32| { - if idx >= 0 { - gui_elements_cloned.borrow_mut().remove_vram_file(idx as usize); } }); + + let gui_elements_cloned = gui_elements.clone(); + main_window.on_main_tab_remove_file_clicked(move |idx: i32| { + let mut gui_elements = gui_elements_cloned.borrow_mut(); + if idx >= 0 { + gui_elements.remove_vram_file(idx as usize); + } + }); + let gui_elements_cloned = gui_elements.clone(); main_window.on_move_vram_image(move |idx: i32, dx: i32, dy: i32| { - gui_elements_cloned.borrow_mut().move_vram_image(idx as usize, dx, dy); + let mut gui_elements = gui_elements_cloned.borrow_mut(); + gui_elements.move_vram_image(idx as usize, dx, dy); + }); + + let gui_elements_cloned = gui_elements.clone(); + main_window.on_file_tab_browse_convert_image(move || { + let gui_elements = gui_elements_cloned.borrow_mut(); + let file = FileDialog::new() + .add_filter("Images", &["png", "bmp", "jpeg"]) + .add_filter("All", &[""]) + .set_title("Select image file") + .pick_file(); + + if let Some(file) = file { + let main_window = gui_elements.main_window.borrow(); + if let Some(file_path) = file.to_str() { + main_window.set_file_tab_browse_path(SharedString::from(file_path)); + } + } }); main_window.run() diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint index 88453d12..5ace34ad 100644 --- a/src/Tools/tim_tool/ui/app-window.slint +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -13,6 +13,10 @@ export component MainWindow inherits Window { callback main_tab_remove_file_clicked <=> main_tab.remove_file_clicked; callback move_vram_image <=> main_tab.move_vram_image; + // Convert Image values + in-out property file_tab_browse_path <=> file_tab.conv_image_path; + callback file_tab_browse_convert_image <=> file_tab.conv_image_browse_clicked; + title: "TIM Tool 0.1.0"; width: tab_widget.width; height: tab_widget.height; @@ -26,7 +30,7 @@ export component MainWindow inherits Window { current-index: 1; Tab { title: "File"; - FileTab { + file_tab := FileTab { x: 0px; y: 0px; } diff --git a/src/Tools/tim_tool/ui/tab/file-tab.slint b/src/Tools/tim_tool/ui/tab/file-tab.slint index 50f51cfa..995a1dc0 100644 --- a/src/Tools/tim_tool/ui/tab/file-tab.slint +++ b/src/Tools/tim_tool/ui/tab/file-tab.slint @@ -1,3 +1,94 @@ -export component FileTab inherits Rectangle { +import { Button, TabWidget, LineEdit, GroupBox } from "std-widgets.slint"; +enum State { + ConvertImage, + Test +} + +component ConvertImageWidget inherits Rectangle { + in-out property path; + + callback browse_clicked(); + + background: #D0D0D0; + width: 100%; + height: 100%; + + GroupBox { + title: "Convert image to TIM"; + x: 4px; + y: 4px; + + VerticalLayout { + alignment: start; + Text { + text: "Select image file to convert to TIM"; + } + LineEdit { + width: 200%; + text: path; + } + HorizontalLayout { + alignment: start; + padding: 4px; + Button { + text: "Browse"; + clicked => {browse_clicked();} + } + Button { + text: "Convert"; + } + } + } + } +} + +component TestWidget inherits Rectangle { + Text { + text: "!!Planschbecken!!"; + } +} + +export component FileTab inherits Rectangle { + in-out property conv_image_path; + callback conv_image_browse_clicked; + + property state: ConvertImage; + x: 0px; + y: 0px; + + HorizontalLayout { + padding: 4px; + VerticalLayout { + padding: 4px; + width: 20%; + alignment: start; + + Button { + text: "Convert image file"; + clicked => { + root.state = State.ConvertImage; + } + } + Button { + text: "Testing"; + clicked => { + root.state = State.Test; + } + } + } + + VerticalLayout { + padding: 4px; + alignment: start; + if root.state == State.ConvertImage : ConvertImageWidget { + path <=> root.conv_image_path; + browse_clicked => { + root.conv_image_browse_clicked(); + } + } + if root.state == State.Test : TestWidget { + } + } + } } \ No newline at end of file diff --git a/src/Tools/tim_tool/ui/tab/main-tab.slint b/src/Tools/tim_tool/ui/tab/main-tab.slint index a91d7326..213ad5af 100644 --- a/src/Tools/tim_tool/ui/tab/main-tab.slint +++ b/src/Tools/tim_tool/ui/tab/main-tab.slint @@ -91,7 +91,7 @@ export component MainTab inherits Rectangle { HorizontalLayout { padding: 4px; GroupBox { - title: "Added Files"; + title: "Added TIMs"; VerticalLayout { alignment: start; padding: 4px; @@ -103,11 +103,11 @@ export component MainTab inherits Rectangle { HorizontalLayout { padding: 4px; Button { - text: "Add File"; + text: "Add TIM"; clicked => {root.add_file_clicked();} } Button { - text: "Remove File"; + text: "Remove TIM"; clicked => { root.remove_file_clicked(vram_files_list.current_item); vram_files_list.current-item = -1; From 9146bf94a549d5112af01187a9cbe54078601a71 Mon Sep 17 00:00:00 2001 From: Jaby Date: Wed, 12 Feb 2025 21:47:38 +0100 Subject: [PATCH 18/32] Restructure code a bit --- src/Tools/tim_tool/src/gui/gui_elements.rs | 78 +++++++++++++++++++++ src/Tools/tim_tool/src/gui/mod.rs | 4 ++ src/Tools/tim_tool/src/main.rs | 79 +--------------------- 3 files changed, 85 insertions(+), 76 deletions(-) create mode 100644 src/Tools/tim_tool/src/gui/gui_elements.rs diff --git a/src/Tools/tim_tool/src/gui/gui_elements.rs b/src/Tools/tim_tool/src/gui/gui_elements.rs new file mode 100644 index 00000000..d26ec7bd --- /dev/null +++ b/src/Tools/tim_tool/src/gui/gui_elements.rs @@ -0,0 +1,78 @@ +use crate::{gui::{VRAM_HEIGHT, VRAM_WIDTH, create_vram_bg}, MainWindow, VRAMImage}; + +use slint::Model; +use std::{cell::RefCell, ffi::OsStr, path::PathBuf, rc::Rc}; +use tool_helper::Error; + +pub struct GUIElements { + pub main_window: Rc>, + vram_file_list: Rc>, + vram_image_list: Rc>, +} + +impl GUIElements { + pub fn new(main_window: Rc>) -> GUIElements { + let vram_file_list:Vec = main_window.borrow().get_main_tab_vram_file_list().iter().collect(); + let vram_file_list = Rc::new(slint::VecModel::from(vram_file_list)); + + let vram_image_list = Rc::new(slint::VecModel::from(Vec::::new())); + + main_window.borrow().set_main_tab_vram_file_list(vram_file_list.clone().into()); + main_window.borrow().set_main_tab_vram_images(vram_image_list.clone().into()); + + GUIElements{main_window, vram_file_list, vram_image_list} + } + + pub fn add_new_vram_file(&mut self, file: PathBuf) -> Result<(), Error> { + let vram_image = VRAMImage{ + img: slint::Image::load_from_path(&file).or_else(|_| {Err(Error::from_str("Failed loading image"))})?, + x: 0, + y: 0 + }; + + let img_size = vram_image.img.size(); + if img_size.width > VRAM_WIDTH as u32 || img_size.height > VRAM_HEIGHT as u32 { + return Err(Error::from_text(format!("Image size ({}; {}) is to big for VRAM ({}, {})", img_size.width, img_size.height, VRAM_WIDTH, VRAM_HEIGHT))); + } + + let file_name = file.file_name().get_or_insert(OsStr::new("")).to_string_lossy().to_string(); + self.vram_file_list.push(slint::StandardListViewItem::from(file_name.as_str())); + self.vram_image_list.push(vram_image); + Ok(()) + } + + pub fn remove_vram_file(&mut self, idx: usize) { + self.vram_file_list.remove(idx); + self.vram_image_list.remove(idx); + } + + pub fn move_vram_image(&mut self, idx: usize, dx: i32, dy: i32) { + if let Some(mut vram_info) = self.vram_image_list.row_data(idx) { + vram_info.x += dx; + vram_info.y += dy; + + if vram_info.x < 0 { + vram_info.x = 0; + } + + if vram_info.y < 0 { + vram_info.y = 0; + } + + let (vram_img_width, vram_img_height) = (vram_info.img.size().width as i32, vram_info.img.size().height as i32); + if (vram_info.x + vram_img_width) > VRAM_WIDTH as i32 { + vram_info.x = VRAM_WIDTH as i32 - vram_img_width; + } + + if (vram_info.y + vram_img_height) > VRAM_HEIGHT as i32 { + vram_info.y = VRAM_HEIGHT as i32 - vram_img_height; + } + + self.vram_image_list.set_row_data(idx, vram_info); + } + } + + pub fn create_bg() -> Result { + Ok(slint::Image::from_rgba8_premultiplied(create_vram_bg(320, 256).ok_or(slint::PlatformError::Other("Failed creating VRAM background image".to_string()))?)) + } +} \ No newline at end of file diff --git a/src/Tools/tim_tool/src/gui/mod.rs b/src/Tools/tim_tool/src/gui/mod.rs index fe1dec0d..82b6483c 100644 --- a/src/Tools/tim_tool/src/gui/mod.rs +++ b/src/Tools/tim_tool/src/gui/mod.rs @@ -1,6 +1,10 @@ +mod gui_elements; + use slint::{Rgba8Pixel, SharedPixelBuffer}; use tiny_skia::{Rect, Transform}; +pub use gui_elements::GUIElements; + pub const VRAM_WIDTH:usize = 1024; pub const VRAM_HEIGHT:usize = 512; diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index 6e14a374..1464fb5b 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -2,86 +2,13 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] mod gui; -use slint::{Model, SharedString}; +use gui::GUIElements; use rfd::{FileDialog, MessageDialog}; -use std::{cell::RefCell, ffi::OsStr, path::PathBuf, rc::Rc}; -use tool_helper::Error; +use std::{cell::RefCell, rc::Rc}; +use slint::SharedString; slint::include_modules!(); -struct GUIElements { - pub main_window: Rc>, - vram_file_list: Rc>, - vram_image_list: Rc>, -} - -impl GUIElements { - pub fn new(main_window: Rc>) -> GUIElements { - let vram_file_list:Vec = main_window.borrow().get_main_tab_vram_file_list().iter().collect(); - let vram_file_list = Rc::new(slint::VecModel::from(vram_file_list)); - - let vram_image_list = Rc::new(slint::VecModel::from(Vec::::new())); - - main_window.borrow().set_main_tab_vram_file_list(vram_file_list.clone().into()); - main_window.borrow().set_main_tab_vram_images(vram_image_list.clone().into()); - - GUIElements{main_window, vram_file_list, vram_image_list} - } - - pub fn add_new_vram_file(&mut self, file: PathBuf) -> Result<(), Error> { - let vram_image = VRAMImage{ - img: slint::Image::load_from_path(&file).or_else(|_| {Err(Error::from_str("Failed loading image"))})?, - x: 0, - y: 0 - }; - - let img_size = vram_image.img.size(); - if img_size.width > gui::VRAM_WIDTH as u32 || img_size.height > gui::VRAM_HEIGHT as u32 { - return Err(Error::from_text(format!("Image size ({}; {}) is to big for VRAM ({}, {})", img_size.width, img_size.height, gui::VRAM_WIDTH, gui::VRAM_HEIGHT))); - } - - let file_name = file.file_name().get_or_insert(OsStr::new("")).to_string_lossy().to_string(); - self.vram_file_list.push(slint::StandardListViewItem::from(file_name.as_str())); - self.vram_image_list.push(vram_image); - Ok(()) - } - - pub fn remove_vram_file(&mut self, idx: usize) { - self.vram_file_list.remove(idx); - self.vram_image_list.remove(idx); - } - - pub fn move_vram_image(&mut self, idx: usize, dx: i32, dy: i32) { - if let Some(mut vram_info) = self.vram_image_list.row_data(idx) { - vram_info.x += dx; - vram_info.y += dy; - - if vram_info.x < 0 { - vram_info.x = 0; - } - - if vram_info.y < 0 { - vram_info.y = 0; - } - - let (vram_img_width, vram_img_height) = (vram_info.img.size().width as i32, vram_info.img.size().height as i32); - if (vram_info.x + vram_img_width) > gui::VRAM_WIDTH as i32 { - vram_info.x = gui::VRAM_WIDTH as i32 - vram_img_width; - } - - if (vram_info.y + vram_img_height) > gui::VRAM_HEIGHT as i32 { - vram_info.y = gui::VRAM_HEIGHT as i32 - vram_img_height; - } - - self.vram_image_list.set_row_data(idx, vram_info); - } - } - - pub fn create_bg() -> Result { - Ok(slint::Image::from_rgba8_premultiplied(gui::create_vram_bg(320, 256).ok_or(slint::PlatformError::Other("Failed creating VRAM background image".to_string()))?)) - } -} - fn main() -> Result<(), slint::PlatformError> { let main_window = Rc::new(RefCell::new(MainWindow::new()?)); let gui_elements = Rc::new(RefCell::new(GUIElements::new(main_window.clone()))); From 13c051715eed67040aeea9d2224e006615839d3d Mon Sep 17 00:00:00 2001 From: Jaby Date: Wed, 12 Feb 2025 22:22:40 +0100 Subject: [PATCH 19/32] Support bundeling applications; Set icon for TIM Tool --- podman/scripts/install_rust.sh | 2 +- src/Tools/Tools.code-workspace | 2 +- src/Tools/tim_tool/Cargo.toml | 11 +++++++++++ src/Tools/tim_tool/ui/assets/TimTool64x64.png | 3 +++ 4 files changed, 16 insertions(+), 2 deletions(-) create mode 100644 src/Tools/tim_tool/ui/assets/TimTool64x64.png diff --git a/podman/scripts/install_rust.sh b/podman/scripts/install_rust.sh index 0d515eb1..067e610c 100755 --- a/podman/scripts/install_rust.sh +++ b/podman/scripts/install_rust.sh @@ -6,4 +6,4 @@ RUST_VERSION=1.84.0 echo "<<< Install Rust $RUST_VERSION >>>" curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain=$RUST_VERSION -y . "$HOME/.cargo/env" -cargo install cargo-edit --locked \ No newline at end of file +cargo install cargo-edit cargo-bundle --locked \ No newline at end of file diff --git a/src/Tools/Tools.code-workspace b/src/Tools/Tools.code-workspace index a9265339..49672a22 100644 --- a/src/Tools/Tools.code-workspace +++ b/src/Tools/Tools.code-workspace @@ -50,7 +50,7 @@ { "id": "cargo cmd", "type":"pickString", - "options": ["build", "check", "upgrade", "clean", "run", "tree"], + "options": ["build", "check", "run", "upgrade", "clean", "bundle"], "default": "build", "description": "cargo command to run" } diff --git a/src/Tools/tim_tool/Cargo.toml b/src/Tools/tim_tool/Cargo.toml index f07694dc..dd0b80c4 100644 --- a/src/Tools/tim_tool/Cargo.toml +++ b/src/Tools/tim_tool/Cargo.toml @@ -14,3 +14,14 @@ tool_helper = {path = "../tool_helper"} [build-dependencies] slint-build = "1.9.2" + +[package.metadata.bundle] +name = "TIMTOOL" +identifier = "zone.jabyengine.timtool" +icon = ["ui/assets/TimTool64x64.png"] +copyright = "Copyright (c) 2025 Jaby MIT License" +category = "Developer Tool" +short_description = "TIM Tool for JabyEngine" +long_description = """ +A new interpetation of the TIM Tool from Sony, for use with PSYQ and the JabyEngine +""" \ No newline at end of file diff --git a/src/Tools/tim_tool/ui/assets/TimTool64x64.png b/src/Tools/tim_tool/ui/assets/TimTool64x64.png new file mode 100644 index 00000000..826eeb6f --- /dev/null +++ b/src/Tools/tim_tool/ui/assets/TimTool64x64.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2ee6873c7e18715367d9eec6da7fa56532ed23985cea21905acdf5f20a52007f +size 1703 From b35967d6dc97da861d65884be3f40f3b101b3070 Mon Sep 17 00:00:00 2001 From: Jaby Date: Thu, 13 Feb 2025 22:33:52 +0100 Subject: [PATCH 20/32] Clean-up ui code --- src/Tools/tim_tool/src/gui/file_tab.rs | 19 +++++ src/Tools/tim_tool/src/gui/gui_elements.rs | 76 ++--------------- src/Tools/tim_tool/src/gui/main_tab.rs | 98 ++++++++++++++++++++++ src/Tools/tim_tool/src/gui/mod.rs | 9 +- src/Tools/tim_tool/src/main.rs | 56 ++++++------- 5 files changed, 162 insertions(+), 96 deletions(-) create mode 100644 src/Tools/tim_tool/src/gui/file_tab.rs create mode 100644 src/Tools/tim_tool/src/gui/main_tab.rs diff --git a/src/Tools/tim_tool/src/gui/file_tab.rs b/src/Tools/tim_tool/src/gui/file_tab.rs new file mode 100644 index 00000000..49c29a87 --- /dev/null +++ b/src/Tools/tim_tool/src/gui/file_tab.rs @@ -0,0 +1,19 @@ +use crate::MainWindow; +use super::MainWindowRef; + +pub struct FileTab { + main_window: MainWindowRef +} + +impl FileTab { + pub fn new(main_window: MainWindowRef) -> FileTab { + FileTab{main_window} + } + + pub fn on_browse_file(&self, mut function: impl FnMut(&MainWindow) + 'static) { + let main_window_cloned = self.main_window.clone(); + self.main_window.borrow().on_file_tab_browse_convert_image(move || { + function(&main_window_cloned.borrow()); + }); + } +} \ No newline at end of file diff --git a/src/Tools/tim_tool/src/gui/gui_elements.rs b/src/Tools/tim_tool/src/gui/gui_elements.rs index d26ec7bd..da0036e2 100644 --- a/src/Tools/tim_tool/src/gui/gui_elements.rs +++ b/src/Tools/tim_tool/src/gui/gui_elements.rs @@ -1,78 +1,20 @@ -use crate::{gui::{VRAM_HEIGHT, VRAM_WIDTH, create_vram_bg}, MainWindow, VRAMImage}; +use super::create_vram_bg; -use slint::Model; -use std::{cell::RefCell, ffi::OsStr, path::PathBuf, rc::Rc}; -use tool_helper::Error; +use super::MainWindowRef; +use super::{file_tab::FileTab, main_tab::MainTab}; pub struct GUIElements { - pub main_window: Rc>, - vram_file_list: Rc>, - vram_image_list: Rc>, + pub file_tab: FileTab, + pub main_tab: MainTab, } impl GUIElements { - pub fn new(main_window: Rc>) -> GUIElements { - let vram_file_list:Vec = main_window.borrow().get_main_tab_vram_file_list().iter().collect(); - let vram_file_list = Rc::new(slint::VecModel::from(vram_file_list)); - - let vram_image_list = Rc::new(slint::VecModel::from(Vec::::new())); - - main_window.borrow().set_main_tab_vram_file_list(vram_file_list.clone().into()); - main_window.borrow().set_main_tab_vram_images(vram_image_list.clone().into()); - - GUIElements{main_window, vram_file_list, vram_image_list} + pub fn new(main_window: MainWindowRef) -> Result { + main_window.borrow().set_main_tab_vram_bg(GUIElements::create_bg()?); + Ok(GUIElements{file_tab: FileTab::new(main_window.clone()), main_tab: MainTab::new(main_window.clone())}) } - pub fn add_new_vram_file(&mut self, file: PathBuf) -> Result<(), Error> { - let vram_image = VRAMImage{ - img: slint::Image::load_from_path(&file).or_else(|_| {Err(Error::from_str("Failed loading image"))})?, - x: 0, - y: 0 - }; - - let img_size = vram_image.img.size(); - if img_size.width > VRAM_WIDTH as u32 || img_size.height > VRAM_HEIGHT as u32 { - return Err(Error::from_text(format!("Image size ({}; {}) is to big for VRAM ({}, {})", img_size.width, img_size.height, VRAM_WIDTH, VRAM_HEIGHT))); - } - - let file_name = file.file_name().get_or_insert(OsStr::new("")).to_string_lossy().to_string(); - self.vram_file_list.push(slint::StandardListViewItem::from(file_name.as_str())); - self.vram_image_list.push(vram_image); - Ok(()) - } - - pub fn remove_vram_file(&mut self, idx: usize) { - self.vram_file_list.remove(idx); - self.vram_image_list.remove(idx); - } - - pub fn move_vram_image(&mut self, idx: usize, dx: i32, dy: i32) { - if let Some(mut vram_info) = self.vram_image_list.row_data(idx) { - vram_info.x += dx; - vram_info.y += dy; - - if vram_info.x < 0 { - vram_info.x = 0; - } - - if vram_info.y < 0 { - vram_info.y = 0; - } - - let (vram_img_width, vram_img_height) = (vram_info.img.size().width as i32, vram_info.img.size().height as i32); - if (vram_info.x + vram_img_width) > VRAM_WIDTH as i32 { - vram_info.x = VRAM_WIDTH as i32 - vram_img_width; - } - - if (vram_info.y + vram_img_height) > VRAM_HEIGHT as i32 { - vram_info.y = VRAM_HEIGHT as i32 - vram_img_height; - } - - self.vram_image_list.set_row_data(idx, vram_info); - } - } - - pub fn create_bg() -> Result { + fn create_bg() -> Result { Ok(slint::Image::from_rgba8_premultiplied(create_vram_bg(320, 256).ok_or(slint::PlatformError::Other("Failed creating VRAM background image".to_string()))?)) } } \ No newline at end of file diff --git a/src/Tools/tim_tool/src/gui/main_tab.rs b/src/Tools/tim_tool/src/gui/main_tab.rs new file mode 100644 index 00000000..5c45adc3 --- /dev/null +++ b/src/Tools/tim_tool/src/gui/main_tab.rs @@ -0,0 +1,98 @@ +use crate::{gui::{VRAM_HEIGHT, VRAM_WIDTH}, MainWindow, VRAMImage}; +use super::{GUIElementsRef, MainWindowRef}; + +use slint::Model; +use std::{ffi::OsStr, path::PathBuf, rc::Rc}; +use tool_helper::Error; + +pub struct MainTab { + main_window: MainWindowRef, + vram_file_list: Rc>, + vram_image_list: Rc>, +} + +impl MainTab { + pub fn new(main_window: MainWindowRef) -> MainTab { + let vram_file_list:Vec = main_window.borrow().get_main_tab_vram_file_list().iter().collect(); + let vram_file_list = Rc::new(slint::VecModel::from(vram_file_list)); + let vram_image_list = Rc::new(slint::VecModel::from(Vec::::new())); + + main_window.borrow().set_main_tab_vram_file_list(vram_file_list.clone().into()); + main_window.borrow().set_main_tab_vram_images(vram_image_list.clone().into()); + + MainTab{main_window, vram_file_list, vram_image_list} + } + + pub fn add_new_vram_file(&mut self, file: PathBuf) -> Result<(), Error> { + let vram_image = VRAMImage{ + img: slint::Image::load_from_path(&file).or_else(|_| {Err(Error::from_str("Failed loading image"))})?, + x: 0, + y: 0 + }; + + let img_size = vram_image.img.size(); + if img_size.width > VRAM_WIDTH as u32 || img_size.height > VRAM_HEIGHT as u32 { + return Err(Error::from_text(format!("Image size ({}; {}) is to big for VRAM ({}, {})", img_size.width, img_size.height, VRAM_WIDTH, VRAM_HEIGHT))); + } + + let file_name = file.file_name().get_or_insert(OsStr::new("")).to_string_lossy().to_string(); + self.vram_file_list.push(slint::StandardListViewItem::from(file_name.as_str())); + self.vram_image_list.push(vram_image); + Ok(()) + } + + pub fn remove_vram_file(&mut self, idx: usize) { + self.vram_file_list.remove(idx); + self.vram_image_list.remove(idx); + } + + pub fn move_vram_image(&mut self, idx: usize, dx: i32, dy: i32) { + if let Some(mut vram_info) = self.vram_image_list.row_data(idx) { + vram_info.x += dx; + vram_info.y += dy; + + if vram_info.x < 0 { + vram_info.x = 0; + } + + if vram_info.y < 0 { + vram_info.y = 0; + } + + let (vram_img_width, vram_img_height) = (vram_info.img.size().width as i32, vram_info.img.size().height as i32); + if (vram_info.x + vram_img_width) > VRAM_WIDTH as i32 { + vram_info.x = VRAM_WIDTH as i32 - vram_img_width; + } + + if (vram_info.y + vram_img_height) > VRAM_HEIGHT as i32 { + vram_info.y = VRAM_HEIGHT as i32 - vram_img_height; + } + + self.vram_image_list.set_row_data(idx, vram_info); + } + } + + pub fn on_move_vram_image(&self, gui_elements: GUIElementsRef, mut function: impl FnMut(&mut MainTab, &MainWindow, i32, i32, i32) + 'static) { + let main_window_cloned = self.main_window.clone(); + let gui_cloned = gui_elements.clone(); + self.main_window.borrow().on_move_vram_image(move |idx, dx, dy| { + function(&mut gui_cloned.borrow_mut().main_tab, &main_window_cloned.borrow(), idx, dx, dy); + }); + } + + pub fn on_remove_file(&self, gui_elements: GUIElementsRef, mut function: impl FnMut(&mut MainTab, &MainWindow, i32) + 'static) { + let main_window_cloned = self.main_window.clone(); + let gui_cloned = gui_elements.clone(); + self.main_window.borrow().on_main_tab_remove_file_clicked(move |idx| { + function(&mut gui_cloned.borrow_mut().main_tab, &main_window_cloned.borrow(), idx); + }); + } + + pub fn on_add_file(&self, gui_elements: GUIElementsRef, mut function: impl FnMut(&mut MainTab, &MainWindow) + 'static) { + let main_window_cloned = self.main_window.clone(); + let gui_cloned = gui_elements.clone(); + self.main_window.borrow().on_main_tab_add_file_clicked(move || { + function(&mut gui_cloned.borrow_mut().main_tab, &main_window_cloned.borrow()); + }); + } +} \ No newline at end of file diff --git a/src/Tools/tim_tool/src/gui/mod.rs b/src/Tools/tim_tool/src/gui/mod.rs index 82b6483c..163db6ce 100644 --- a/src/Tools/tim_tool/src/gui/mod.rs +++ b/src/Tools/tim_tool/src/gui/mod.rs @@ -1,5 +1,9 @@ +mod file_tab; mod gui_elements; +mod main_tab; +use crate::MainWindow; +use std::{cell::RefCell, rc::Rc}; use slint::{Rgba8Pixel, SharedPixelBuffer}; use tiny_skia::{Rect, Transform}; @@ -8,7 +12,10 @@ pub use gui_elements::GUIElements; pub const VRAM_WIDTH:usize = 1024; pub const VRAM_HEIGHT:usize = 512; -pub fn create_vram_bg(rect_width: u32, rect_height: u32) -> Option> { +type MainWindowRef = Rc>; +type GUIElementsRef = Rc>; + +fn create_vram_bg(rect_width: u32, rect_height: u32) -> Option> { let mut pixel_buffer = SharedPixelBuffer::::new(VRAM_WIDTH as u32, VRAM_HEIGHT as u32); let width = pixel_buffer.width(); let height = pixel_buffer.height(); diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index 1464fb5b..65c6861b 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -10,45 +10,48 @@ use slint::SharedString; slint::include_modules!(); fn main() -> Result<(), slint::PlatformError> { - let main_window = Rc::new(RefCell::new(MainWindow::new()?)); - let gui_elements = Rc::new(RefCell::new(GUIElements::new(main_window.clone()))); - let main_window = main_window.borrow(); + let main_window_ref = Rc::new(RefCell::new(MainWindow::new()?)); + let gui_elements_ref = Rc::new(RefCell::new(GUIElements::new(main_window_ref.clone())?)); - main_window.set_main_tab_vram_bg(GUIElements::create_bg()?); + setup_main_tab(gui_elements_ref.clone()); + setup_file_tab(gui_elements_ref.clone()); - let gui_elements_cloned = gui_elements.clone(); - main_window.on_main_tab_add_file_clicked(move || { - let mut gui_elements = gui_elements_cloned.borrow_mut(); - let file = FileDialog::new() + let main_window = main_window_ref.borrow(); + main_window.run() +} + +fn setup_main_tab(gui_elements_ref: Rc>) { + let gui_elements = gui_elements_ref.borrow(); + + gui_elements.main_tab.on_move_vram_image(gui_elements_ref.clone(), move |main_tab, _main_window, idx, dx, dy| { + main_tab.move_vram_image(idx as usize, dx, dy); + }); + + gui_elements.main_tab.on_remove_file(gui_elements_ref.clone(), move |main_tab, _main_window, idx| { + if idx >= 0 { + main_tab.remove_vram_file(idx as usize); + } + }); + + gui_elements.main_tab.on_add_file(gui_elements_ref.clone(), move |main_tab, _main_window| { + let file = FileDialog::new() .add_filter("Images", &["png", "bmp", "jpeg"]) .add_filter("All", &[""]) .set_title("Add TIM image") .pick_file(); if let Some(file) = file { - if let Result::Err(error) = gui_elements.add_new_vram_file(file) { + if let Result::Err(error) = main_tab.add_new_vram_file(file) { MessageDialog::new().set_title("Loading Image failed").set_level(rfd::MessageLevel::Error).set_description(error.to_string()).show(); } } }); +} - let gui_elements_cloned = gui_elements.clone(); - main_window.on_main_tab_remove_file_clicked(move |idx: i32| { - let mut gui_elements = gui_elements_cloned.borrow_mut(); - if idx >= 0 { - gui_elements.remove_vram_file(idx as usize); - } - }); +fn setup_file_tab(gui_elements_ref: Rc>) { + let gui_elements = gui_elements_ref.borrow(); - let gui_elements_cloned = gui_elements.clone(); - main_window.on_move_vram_image(move |idx: i32, dx: i32, dy: i32| { - let mut gui_elements = gui_elements_cloned.borrow_mut(); - gui_elements.move_vram_image(idx as usize, dx, dy); - }); - - let gui_elements_cloned = gui_elements.clone(); - main_window.on_file_tab_browse_convert_image(move || { - let gui_elements = gui_elements_cloned.borrow_mut(); + gui_elements.file_tab.on_browse_file(move |main_window| { let file = FileDialog::new() .add_filter("Images", &["png", "bmp", "jpeg"]) .add_filter("All", &[""]) @@ -56,12 +59,9 @@ fn main() -> Result<(), slint::PlatformError> { .pick_file(); if let Some(file) = file { - let main_window = gui_elements.main_window.borrow(); if let Some(file_path) = file.to_str() { main_window.set_file_tab_browse_path(SharedString::from(file_path)); } } }); - - main_window.run() } \ No newline at end of file From 88c8923989abc9eab9aa9f893c20ff906def68ae Mon Sep 17 00:00:00 2001 From: Jaby Date: Sat, 15 Feb 2025 10:11:06 +0100 Subject: [PATCH 21/32] Add UI for project approach --- src/Tools/tim_tool/ui/tab/file-tab.slint | 26 ++++++++++++------------ src/Tools/tim_tool/ui/tab/main-tab.slint | 6 +++--- 2 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/Tools/tim_tool/ui/tab/file-tab.slint b/src/Tools/tim_tool/ui/tab/file-tab.slint index 995a1dc0..d265f56f 100644 --- a/src/Tools/tim_tool/ui/tab/file-tab.slint +++ b/src/Tools/tim_tool/ui/tab/file-tab.slint @@ -1,11 +1,11 @@ import { Button, TabWidget, LineEdit, GroupBox } from "std-widgets.slint"; enum State { - ConvertImage, - Test + OpenProject, + SaveProject } -component ConvertImageWidget inherits Rectangle { +component OpenProjectWidget inherits Rectangle { in-out property path; callback browse_clicked(); @@ -15,14 +15,14 @@ component ConvertImageWidget inherits Rectangle { height: 100%; GroupBox { - title: "Convert image to TIM"; + title: "Open project"; x: 4px; y: 4px; VerticalLayout { alignment: start; Text { - text: "Select image file to convert to TIM"; + text: "Select project file to open with TIM Tool"; } LineEdit { width: 200%; @@ -43,7 +43,7 @@ component ConvertImageWidget inherits Rectangle { } } -component TestWidget inherits Rectangle { +component SaveProjectWidget inherits Rectangle { Text { text: "!!Planschbecken!!"; } @@ -53,7 +53,7 @@ export component FileTab inherits Rectangle { in-out property conv_image_path; callback conv_image_browse_clicked; - property state: ConvertImage; + property state; x: 0px; y: 0px; @@ -65,15 +65,15 @@ export component FileTab inherits Rectangle { alignment: start; Button { - text: "Convert image file"; + text: "Open project"; clicked => { - root.state = State.ConvertImage; + root.state = State.OpenProject; } } Button { - text: "Testing"; + text: "Save project"; clicked => { - root.state = State.Test; + root.state = State.SaveProject; } } } @@ -81,13 +81,13 @@ export component FileTab inherits Rectangle { VerticalLayout { padding: 4px; alignment: start; - if root.state == State.ConvertImage : ConvertImageWidget { + if root.state == State.OpenProject : OpenProjectWidget { path <=> root.conv_image_path; browse_clicked => { root.conv_image_browse_clicked(); } } - if root.state == State.Test : TestWidget { + if root.state == State.SaveProject : SaveProjectWidget { } } } diff --git a/src/Tools/tim_tool/ui/tab/main-tab.slint b/src/Tools/tim_tool/ui/tab/main-tab.slint index 213ad5af..ae73d468 100644 --- a/src/Tools/tim_tool/ui/tab/main-tab.slint +++ b/src/Tools/tim_tool/ui/tab/main-tab.slint @@ -91,7 +91,7 @@ export component MainTab inherits Rectangle { HorizontalLayout { padding: 4px; GroupBox { - title: "Added TIMs"; + title: "Added files"; VerticalLayout { alignment: start; padding: 4px; @@ -103,11 +103,11 @@ export component MainTab inherits Rectangle { HorizontalLayout { padding: 4px; Button { - text: "Add TIM"; + text: "Add file"; clicked => {root.add_file_clicked();} } Button { - text: "Remove TIM"; + text: "Remove file"; clicked => { root.remove_file_clicked(vram_files_list.current_item); vram_files_list.current-item = -1; From c237a9af45331f4b76ef6fa94bfb827cc708dcd5 Mon Sep 17 00:00:00 2001 From: Jaby Date: Sat, 15 Feb 2025 11:16:49 +0100 Subject: [PATCH 22/32] Redirect to file dialog --- src/Tools/tim_tool/src/gui/file_tab.rs | 8 +++-- src/Tools/tim_tool/src/gui/main_tab.rs | 8 ----- src/Tools/tim_tool/src/main.rs | 26 +++++------------ src/Tools/tim_tool/ui/app-window.slint | 13 +++++++-- src/Tools/tim_tool/ui/tab/file-tab.slint | 37 ++++++++++++------------ 5 files changed, 43 insertions(+), 49 deletions(-) diff --git a/src/Tools/tim_tool/src/gui/file_tab.rs b/src/Tools/tim_tool/src/gui/file_tab.rs index 49c29a87..1e966325 100644 --- a/src/Tools/tim_tool/src/gui/file_tab.rs +++ b/src/Tools/tim_tool/src/gui/file_tab.rs @@ -1,5 +1,5 @@ use crate::MainWindow; -use super::MainWindowRef; +use super::{GUIElementsRef, MainWindowRef, main_tab::MainTab}; pub struct FileTab { main_window: MainWindowRef @@ -10,10 +10,12 @@ impl FileTab { FileTab{main_window} } - pub fn on_browse_file(&self, mut function: impl FnMut(&MainWindow) + 'static) { + pub fn on_browse_file(&self, gui_elements: GUIElementsRef, mut function: impl FnMut(&MainWindow, &mut MainTab) + 'static) { let main_window_cloned = self.main_window.clone(); + let gui_cloned = gui_elements.clone(); + self.main_window.borrow().on_file_tab_browse_convert_image(move || { - function(&main_window_cloned.borrow()); + function(&main_window_cloned.borrow(), &mut gui_cloned.borrow_mut().main_tab); }); } } \ No newline at end of file diff --git a/src/Tools/tim_tool/src/gui/main_tab.rs b/src/Tools/tim_tool/src/gui/main_tab.rs index 5c45adc3..7d8e8c95 100644 --- a/src/Tools/tim_tool/src/gui/main_tab.rs +++ b/src/Tools/tim_tool/src/gui/main_tab.rs @@ -87,12 +87,4 @@ impl MainTab { function(&mut gui_cloned.borrow_mut().main_tab, &main_window_cloned.borrow(), idx); }); } - - pub fn on_add_file(&self, gui_elements: GUIElementsRef, mut function: impl FnMut(&mut MainTab, &MainWindow) + 'static) { - let main_window_cloned = self.main_window.clone(); - let gui_cloned = gui_elements.clone(); - self.main_window.borrow().on_main_tab_add_file_clicked(move || { - function(&mut gui_cloned.borrow_mut().main_tab, &main_window_cloned.borrow()); - }); - } } \ No newline at end of file diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index 65c6861b..db34e439 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -32,29 +32,14 @@ fn setup_main_tab(gui_elements_ref: Rc>) { main_tab.remove_vram_file(idx as usize); } }); - - gui_elements.main_tab.on_add_file(gui_elements_ref.clone(), move |main_tab, _main_window| { - let file = FileDialog::new() - .add_filter("Images", &["png", "bmp", "jpeg"]) - .add_filter("All", &[""]) - .set_title("Add TIM image") - .pick_file(); - - if let Some(file) = file { - if let Result::Err(error) = main_tab.add_new_vram_file(file) { - MessageDialog::new().set_title("Loading Image failed").set_level(rfd::MessageLevel::Error).set_description(error.to_string()).show(); - } - } - }); } fn setup_file_tab(gui_elements_ref: Rc>) { let gui_elements = gui_elements_ref.borrow(); - gui_elements.file_tab.on_browse_file(move |main_window| { - let file = FileDialog::new() - .add_filter("Images", &["png", "bmp", "jpeg"]) - .add_filter("All", &[""]) + gui_elements.file_tab.on_browse_file(gui_elements_ref.clone(), move |main_window, main_tab| { + let file = FileDialog::new() + .add_filter("Images (.png; .bmp; .jpeg)", &["png", "bmp", "jpeg"]) .set_title("Select image file") .pick_file(); @@ -62,6 +47,11 @@ fn setup_file_tab(gui_elements_ref: Rc>) { if let Some(file_path) = file.to_str() { main_window.set_file_tab_browse_path(SharedString::from(file_path)); } + + if let Result::Err(error) = main_tab.add_new_vram_file(file) { + MessageDialog::new().set_title("Loading Image failed").set_level(rfd::MessageLevel::Error).set_description(error.to_string()).show(); + } + main_window.invoke_change_to_main(); } }); } \ No newline at end of file diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint index 5ace34ad..2e408a2b 100644 --- a/src/Tools/tim_tool/ui/app-window.slint +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -1,5 +1,5 @@ import { AboutTab } from "./tab/about-tab.slint"; -import { FileTab } from "./tab/file-tab.slint"; +import { FileTab, State } from "./tab/file-tab.slint"; import { MainTab } from "./tab/main-tab.slint"; import { TabWidget } from "std-widgets.slint"; @@ -9,7 +9,6 @@ export component MainWindow inherits Window { in-out property main_tab_vram_file_list <=> main_tab.vram_files; in-out property main_tab_vram_images <=> main_tab.vram_images; - callback main_tab_add_file_clicked <=> main_tab.add_file_clicked; callback main_tab_remove_file_clicked <=> main_tab.remove_file_clicked; callback move_vram_image <=> main_tab.move_vram_image; @@ -40,6 +39,7 @@ export component MainWindow inherits Window { main_tab := MainTab { x: 0px; y: 0px; + add_file_clicked => {root.change_to_load_file()} } } Tab { @@ -47,4 +47,13 @@ export component MainWindow inherits Window { AboutTab {} } } + + public function change_to_load_file() { + file_tab.state = State.ConvertImage; + tab_widget.current-index = 0; + } + + public function change_to_main() { + tab_widget.current-index = 1; + } } \ No newline at end of file diff --git a/src/Tools/tim_tool/ui/tab/file-tab.slint b/src/Tools/tim_tool/ui/tab/file-tab.slint index d265f56f..e42a928f 100644 --- a/src/Tools/tim_tool/ui/tab/file-tab.slint +++ b/src/Tools/tim_tool/ui/tab/file-tab.slint @@ -1,11 +1,17 @@ import { Button, TabWidget, LineEdit, GroupBox } from "std-widgets.slint"; -enum State { - OpenProject, - SaveProject +export enum State { + Project, + ConvertImage, } -component OpenProjectWidget inherits Rectangle { +component ProjectWidget inherits Rectangle { + Text { + text: "!!Planschbecken!!"; + } +} + +component ConvertImageWidget inherits Rectangle { in-out property path; callback browse_clicked(); @@ -43,17 +49,11 @@ component OpenProjectWidget inherits Rectangle { } } -component SaveProjectWidget inherits Rectangle { - Text { - text: "!!Planschbecken!!"; - } -} - export component FileTab inherits Rectangle { in-out property conv_image_path; + in-out property state; callback conv_image_browse_clicked; - property state; x: 0px; y: 0px; @@ -65,15 +65,15 @@ export component FileTab inherits Rectangle { alignment: start; Button { - text: "Open project"; + text: "Projects"; clicked => { - root.state = State.OpenProject; + root.state = State.Project; } } Button { - text: "Save project"; + text: "Add Image"; clicked => { - root.state = State.SaveProject; + root.state = State.ConvertImage; } } } @@ -81,14 +81,15 @@ export component FileTab inherits Rectangle { VerticalLayout { padding: 4px; alignment: start; - if root.state == State.OpenProject : OpenProjectWidget { + if root.state == State.Project : ProjectWidget { + } + if root.state == State.ConvertImage : ConvertImageWidget { path <=> root.conv_image_path; browse_clicked => { root.conv_image_browse_clicked(); } } - if root.state == State.SaveProject : SaveProjectWidget { - } + } } } \ No newline at end of file From d72d8bd84a469f81bf25a0f12d5fc668f99de7d2 Mon Sep 17 00:00:00 2001 From: Jaby Date: Sat, 15 Feb 2025 12:03:15 +0100 Subject: [PATCH 23/32] Update UI again --- src/Tools/tim_tool/ui/app-window.slint | 1 + src/Tools/tim_tool/ui/tab/file-tab.slint | 93 +++++++++++++++++------- 2 files changed, 66 insertions(+), 28 deletions(-) diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint index 2e408a2b..d962b68b 100644 --- a/src/Tools/tim_tool/ui/app-window.slint +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -14,6 +14,7 @@ export component MainWindow inherits Window { // Convert Image values in-out property file_tab_browse_path <=> file_tab.conv_image_path; + in-out property file_tab_image_data <=> file_tab.conv_image_data; callback file_tab_browse_convert_image <=> file_tab.conv_image_browse_clicked; title: "TIM Tool 0.1.0"; diff --git a/src/Tools/tim_tool/ui/tab/file-tab.slint b/src/Tools/tim_tool/ui/tab/file-tab.slint index e42a928f..aa97b6f7 100644 --- a/src/Tools/tim_tool/ui/tab/file-tab.slint +++ b/src/Tools/tim_tool/ui/tab/file-tab.slint @@ -12,45 +12,82 @@ component ProjectWidget inherits Rectangle { } component ConvertImageWidget inherits Rectangle { - in-out property path; + in-out property image_path; + in-out property image_data; callback browse_clicked(); background: #D0D0D0; - width: 100%; - height: 100%; - GroupBox { - title: "Open project"; - x: 4px; - y: 4px; - - VerticalLayout { - alignment: start; - Text { - text: "Select project file to open with TIM Tool"; - } - LineEdit { - width: 200%; - text: path; - } - HorizontalLayout { + VerticalLayout { + alignment: start; + GroupBox { + title: "Add image file"; + VerticalLayout { alignment: start; - padding: 4px; - Button { - text: "Browse"; - clicked => {browse_clicked();} + padding: 4px; + Text { + text: "Select image file to convert to be used with TIM Tool"; } - Button { - text: "Convert"; - } - } + HorizontalLayout { + alignment: start; + padding: 4px; + LineEdit { + width: 300pt; + text: image_path; + } + Button { + text: "Browse"; + clicked => {browse_clicked();} + } + } + } + } + + GroupBox { + title: "Loaded image"; + VerticalLayout { + alignment: start; + padding: 4px; + HorizontalLayout { + alignment: center; + Rectangle { + width: 256px; + height: 256px; + background: #000000; + Image { + width: 256px; + height: 256px; + source: root.image_data; + image-fit: contain; + } + } + } + HorizontalLayout { + alignment: start; + VerticalLayout { + alignment: center; + Text { + text: "Name: "; + } + } + LineEdit {} + } + HorizontalLayout { + alignment: start; + Button { + text: "Add Image"; + enabled: false; + } + } + } } } } export component FileTab inherits Rectangle { in-out property conv_image_path; + in-out property conv_image_data; in-out property state; callback conv_image_browse_clicked; @@ -84,12 +121,12 @@ export component FileTab inherits Rectangle { if root.state == State.Project : ProjectWidget { } if root.state == State.ConvertImage : ConvertImageWidget { - path <=> root.conv_image_path; + image_path <=> root.conv_image_path; + image_data <=> root.conv_image_data; browse_clicked => { root.conv_image_browse_clicked(); } } - } } } \ No newline at end of file From d9facb8d61069333edec66509445adfb6a2f3c05 Mon Sep 17 00:00:00 2001 From: Jaby Date: Sat, 15 Feb 2025 13:21:40 +0100 Subject: [PATCH 24/32] Add image preview --- src/Tools/tim_tool/src/gui/file_tab.rs | 18 +++++++++++--- src/Tools/tim_tool/src/gui/main_tab.rs | 14 +++-------- src/Tools/tim_tool/src/lib.rs | 4 +--- src/Tools/tim_tool/src/main.rs | 30 ++++++++++++++++++++---- src/Tools/tim_tool/src/tim_tool/mod.rs | 8 +++++++ src/Tools/tim_tool/ui/app-window.slint | 1 + src/Tools/tim_tool/ui/tab/file-tab.slint | 11 +++++++-- 7 files changed, 62 insertions(+), 24 deletions(-) create mode 100644 src/Tools/tim_tool/src/tim_tool/mod.rs diff --git a/src/Tools/tim_tool/src/gui/file_tab.rs b/src/Tools/tim_tool/src/gui/file_tab.rs index 1e966325..76457569 100644 --- a/src/Tools/tim_tool/src/gui/file_tab.rs +++ b/src/Tools/tim_tool/src/gui/file_tab.rs @@ -1,5 +1,6 @@ use crate::MainWindow; -use super::{GUIElementsRef, MainWindowRef, main_tab::MainTab}; +use super::{GUIElements, GUIElementsRef, MainWindowRef}; +use slint::Image; pub struct FileTab { main_window: MainWindowRef @@ -10,12 +11,23 @@ impl FileTab { FileTab{main_window} } - pub fn on_browse_file(&self, gui_elements: GUIElementsRef, mut function: impl FnMut(&MainWindow, &mut MainTab) + 'static) { + pub fn update_new_load(&self, file_name: Option, image: Image) { + self.main_window.borrow().set_file_tab_image_data(image); + if let Some(file_name) = file_name { + self.main_window.borrow().set_file_tab_image_name(file_name.into()); + } + + else { + self.main_window.borrow().set_file_tab_image_name("".into()); + } + } + + pub fn on_browse_file(&self, gui_elements: GUIElementsRef, mut function: impl FnMut(&mut GUIElements, &MainWindow) + 'static) { let main_window_cloned = self.main_window.clone(); let gui_cloned = gui_elements.clone(); self.main_window.borrow().on_file_tab_browse_convert_image(move || { - function(&main_window_cloned.borrow(), &mut gui_cloned.borrow_mut().main_tab); + function(&mut gui_cloned.borrow_mut(), &main_window_cloned.borrow()); }); } } \ No newline at end of file diff --git a/src/Tools/tim_tool/src/gui/main_tab.rs b/src/Tools/tim_tool/src/gui/main_tab.rs index 7d8e8c95..e0fa5b4b 100644 --- a/src/Tools/tim_tool/src/gui/main_tab.rs +++ b/src/Tools/tim_tool/src/gui/main_tab.rs @@ -2,8 +2,7 @@ use crate::{gui::{VRAM_HEIGHT, VRAM_WIDTH}, MainWindow, VRAMImage}; use super::{GUIElementsRef, MainWindowRef}; use slint::Model; -use std::{ffi::OsStr, path::PathBuf, rc::Rc}; -use tool_helper::Error; +use std::rc::Rc; pub struct MainTab { main_window: MainWindowRef, @@ -23,22 +22,15 @@ impl MainTab { MainTab{main_window, vram_file_list, vram_image_list} } - pub fn add_new_vram_file(&mut self, file: PathBuf) -> Result<(), Error> { + pub fn _add_new_vram_file(&mut self, file_name: String, image: slint::Image) { let vram_image = VRAMImage{ - img: slint::Image::load_from_path(&file).or_else(|_| {Err(Error::from_str("Failed loading image"))})?, + img: image, x: 0, y: 0 }; - let img_size = vram_image.img.size(); - if img_size.width > VRAM_WIDTH as u32 || img_size.height > VRAM_HEIGHT as u32 { - return Err(Error::from_text(format!("Image size ({}; {}) is to big for VRAM ({}, {})", img_size.width, img_size.height, VRAM_WIDTH, VRAM_HEIGHT))); - } - - let file_name = file.file_name().get_or_insert(OsStr::new("")).to_string_lossy().to_string(); self.vram_file_list.push(slint::StandardListViewItem::from(file_name.as_str())); self.vram_image_list.push(vram_image); - Ok(()) } pub fn remove_vram_file(&mut self, idx: usize) { diff --git a/src/Tools/tim_tool/src/lib.rs b/src/Tools/tim_tool/src/lib.rs index c1febacb..8a77e313 100644 --- a/src/Tools/tim_tool/src/lib.rs +++ b/src/Tools/tim_tool/src/lib.rs @@ -1,3 +1 @@ -pub fn hello_world() { - println!("Hello Planschi"); -} \ No newline at end of file +pub mod tim_tool; \ No newline at end of file diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index db34e439..c71c344f 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -2,10 +2,11 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] mod gui; -use gui::GUIElements; +use gui::{GUIElements, VRAM_WIDTH, VRAM_HEIGHT}; use rfd::{FileDialog, MessageDialog}; use std::{cell::RefCell, rc::Rc}; use slint::SharedString; +use tim_tool::tim_tool::load_image; slint::include_modules!(); @@ -37,7 +38,11 @@ fn setup_main_tab(gui_elements_ref: Rc>) { fn setup_file_tab(gui_elements_ref: Rc>) { let gui_elements = gui_elements_ref.borrow(); - gui_elements.file_tab.on_browse_file(gui_elements_ref.clone(), move |main_window, main_tab| { + gui_elements.file_tab.on_browse_file(gui_elements_ref.clone(), move |gui_elements, main_window| { + fn show_error_message(text: String) { + MessageDialog::new().set_title("Loading Image failed").set_level(rfd::MessageLevel::Error).set_description(text).show(); + } + let file = FileDialog::new() .add_filter("Images (.png; .bmp; .jpeg)", &["png", "bmp", "jpeg"]) .set_title("Select image file") @@ -48,10 +53,25 @@ fn setup_file_tab(gui_elements_ref: Rc>) { main_window.set_file_tab_browse_path(SharedString::from(file_path)); } - if let Result::Err(error) = main_tab.add_new_vram_file(file) { - MessageDialog::new().set_title("Loading Image failed").set_level(rfd::MessageLevel::Error).set_description(error.to_string()).show(); + let file_name = if let Some(name) = file.file_name() {Some(name.to_string_lossy().to_string())} else {None}; + let (image, _info) = match load_image(file) { + Ok((image, info)) => (image, info), + Err(error) => { + show_error_message(error.to_string()); + return; + } + }; + + let img_size = image.size(); + if img_size.width > VRAM_WIDTH as u32 || img_size.height > VRAM_HEIGHT as u32 { + show_error_message(format!("Image size ({}; {}) is to big for VRAM ({}, {})", img_size.width, img_size.height, VRAM_WIDTH, VRAM_HEIGHT)); + return; } - main_window.invoke_change_to_main(); + + let file_tab = &mut gui_elements.file_tab; + file_tab.update_new_load(file_name, image); + + //main_window.invoke_change_to_main(); } }); } \ No newline at end of file diff --git a/src/Tools/tim_tool/src/tim_tool/mod.rs b/src/Tools/tim_tool/src/tim_tool/mod.rs new file mode 100644 index 00000000..cce6c8ee --- /dev/null +++ b/src/Tools/tim_tool/src/tim_tool/mod.rs @@ -0,0 +1,8 @@ +use std::path::PathBuf; +use tool_helper::Error; + +pub struct TIMImage {} + +pub fn load_image(path: PathBuf) -> Result<(slint::Image, TIMImage), Error> { + Ok((slint::Image::load_from_path(&path).or_else(|_| {Err(Error::from_str("Failed loading image"))})?, TIMImage{})) +} \ No newline at end of file diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint index d962b68b..45c315b1 100644 --- a/src/Tools/tim_tool/ui/app-window.slint +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -15,6 +15,7 @@ export component MainWindow inherits Window { // Convert Image values in-out property file_tab_browse_path <=> file_tab.conv_image_path; in-out property file_tab_image_data <=> file_tab.conv_image_data; + in-out property file_tab_image_name <=> file_tab.conv_image_name; callback file_tab_browse_convert_image <=> file_tab.conv_image_browse_clicked; title: "TIM Tool 0.1.0"; diff --git a/src/Tools/tim_tool/ui/tab/file-tab.slint b/src/Tools/tim_tool/ui/tab/file-tab.slint index aa97b6f7..f5276b4e 100644 --- a/src/Tools/tim_tool/ui/tab/file-tab.slint +++ b/src/Tools/tim_tool/ui/tab/file-tab.slint @@ -14,6 +14,7 @@ component ProjectWidget inherits Rectangle { component ConvertImageWidget inherits Rectangle { in-out property image_path; in-out property image_data; + in-out property image_name; callback browse_clicked(); @@ -48,7 +49,7 @@ component ConvertImageWidget inherits Rectangle { title: "Loaded image"; VerticalLayout { alignment: start; - padding: 4px; + padding: 4px; HorizontalLayout { alignment: center; Rectangle { @@ -65,16 +66,20 @@ component ConvertImageWidget inherits Rectangle { } HorizontalLayout { alignment: start; + padding: 4px; VerticalLayout { alignment: center; Text { text: "Name: "; } } - LineEdit {} + LineEdit { + text: root.image_name; + } } HorizontalLayout { alignment: start; + padding: 4px; Button { text: "Add Image"; enabled: false; @@ -88,6 +93,7 @@ component ConvertImageWidget inherits Rectangle { export component FileTab inherits Rectangle { in-out property conv_image_path; in-out property conv_image_data; + in-out property conv_image_name; in-out property state; callback conv_image_browse_clicked; @@ -123,6 +129,7 @@ export component FileTab inherits Rectangle { if root.state == State.ConvertImage : ConvertImageWidget { image_path <=> root.conv_image_path; image_data <=> root.conv_image_data; + image_name <=> root.conv_image_name; browse_clicked => { root.conv_image_browse_clicked(); } From 5a4760b272e0eff2259e27c88393d7647a449d41 Mon Sep 17 00:00:00 2001 From: Jaby Date: Sat, 15 Feb 2025 14:17:47 +0100 Subject: [PATCH 25/32] Turn on and off button --- src/Tools/tim_tool/src/gui/file_tab.rs | 27 +++++++++++++++++++++--- src/Tools/tim_tool/src/lib.rs | 2 +- src/Tools/tim_tool/src/logic/mod.rs | 12 +++++++++++ src/Tools/tim_tool/src/logic/tim.rs | 8 +++++++ src/Tools/tim_tool/src/main.rs | 23 ++++++++++++++------ src/Tools/tim_tool/src/tim_tool/mod.rs | 8 ------- src/Tools/tim_tool/ui/app-window.slint | 2 ++ src/Tools/tim_tool/ui/tab/file-tab.slint | 13 +++++++++++- 8 files changed, 76 insertions(+), 19 deletions(-) create mode 100644 src/Tools/tim_tool/src/logic/mod.rs create mode 100644 src/Tools/tim_tool/src/logic/tim.rs delete mode 100644 src/Tools/tim_tool/src/tim_tool/mod.rs diff --git a/src/Tools/tim_tool/src/gui/file_tab.rs b/src/Tools/tim_tool/src/gui/file_tab.rs index 76457569..fa889cbf 100644 --- a/src/Tools/tim_tool/src/gui/file_tab.rs +++ b/src/Tools/tim_tool/src/gui/file_tab.rs @@ -11,15 +11,27 @@ impl FileTab { FileTab{main_window} } + pub fn clear_load(&self) { + let main_window = self.main_window.borrow(); + + main_window.set_file_tab_browse_path("".into()); + main_window.set_file_tab_image_data(Image::default()); + main_window.set_file_tab_image_name("".into()); + main_window.set_file_tab_enable(false); + } + pub fn update_new_load(&self, file_name: Option, image: Image) { - self.main_window.borrow().set_file_tab_image_data(image); + let main_window = self.main_window.borrow(); + + main_window.set_file_tab_image_data(image); if let Some(file_name) = file_name { - self.main_window.borrow().set_file_tab_image_name(file_name.into()); + main_window.set_file_tab_image_name(file_name.into()); } else { - self.main_window.borrow().set_file_tab_image_name("".into()); + main_window.set_file_tab_image_name("".into()); } + main_window.set_file_tab_enable(true); } pub fn on_browse_file(&self, gui_elements: GUIElementsRef, mut function: impl FnMut(&mut GUIElements, &MainWindow) + 'static) { @@ -30,4 +42,13 @@ impl FileTab { function(&mut gui_cloned.borrow_mut(), &main_window_cloned.borrow()); }); } + + pub fn on_add_image(&self, gui_elements: GUIElementsRef, mut function: impl FnMut(&mut GUIElements, &MainWindow) + 'static) { + let main_window_cloned = self.main_window.clone(); + let gui_cloned = gui_elements.clone(); + + self.main_window.borrow().on_file_tab_add_convert_image(move || { + function(&mut gui_cloned.borrow_mut(), &main_window_cloned.borrow()); + }); + } } \ No newline at end of file diff --git a/src/Tools/tim_tool/src/lib.rs b/src/Tools/tim_tool/src/lib.rs index 8a77e313..45a2c471 100644 --- a/src/Tools/tim_tool/src/lib.rs +++ b/src/Tools/tim_tool/src/lib.rs @@ -1 +1 @@ -pub mod tim_tool; \ No newline at end of file +pub mod logic; \ No newline at end of file diff --git a/src/Tools/tim_tool/src/logic/mod.rs b/src/Tools/tim_tool/src/logic/mod.rs new file mode 100644 index 00000000..76929b11 --- /dev/null +++ b/src/Tools/tim_tool/src/logic/mod.rs @@ -0,0 +1,12 @@ +pub mod tim; +use tim::TIMInfo; + +pub struct Logic { + pub unadded_tim: TIMInfo, +} + +impl Logic { + pub fn new() -> Logic { + Logic{unadded_tim: TIMInfo{}} + } +} \ No newline at end of file diff --git a/src/Tools/tim_tool/src/logic/tim.rs b/src/Tools/tim_tool/src/logic/tim.rs new file mode 100644 index 00000000..735bb81e --- /dev/null +++ b/src/Tools/tim_tool/src/logic/tim.rs @@ -0,0 +1,8 @@ +use std::path::PathBuf; +use tool_helper::Error; + +pub struct TIMInfo {} + +pub fn load_image(path: PathBuf) -> Result<(slint::Image, TIMInfo), Error> { + Ok((slint::Image::load_from_path(&path).or_else(|_| {Err(Error::from_str("Failed loading image"))})?, TIMInfo{})) +} \ No newline at end of file diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index c71c344f..3f497eb6 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -6,16 +6,18 @@ use gui::{GUIElements, VRAM_WIDTH, VRAM_HEIGHT}; use rfd::{FileDialog, MessageDialog}; use std::{cell::RefCell, rc::Rc}; use slint::SharedString; -use tim_tool::tim_tool::load_image; +use tim_tool::logic::{tim::load_image, Logic}; slint::include_modules!(); + fn main() -> Result<(), slint::PlatformError> { + let logic_ref = Rc::new(RefCell::new(Logic::new())); let main_window_ref = Rc::new(RefCell::new(MainWindow::new()?)); let gui_elements_ref = Rc::new(RefCell::new(GUIElements::new(main_window_ref.clone())?)); setup_main_tab(gui_elements_ref.clone()); - setup_file_tab(gui_elements_ref.clone()); + setup_file_tab(gui_elements_ref.clone(), logic_ref); let main_window = main_window_ref.borrow(); main_window.run() @@ -35,7 +37,7 @@ fn setup_main_tab(gui_elements_ref: Rc>) { }); } -fn setup_file_tab(gui_elements_ref: Rc>) { +fn setup_file_tab(gui_elements_ref: Rc>, logic_ref: Rc>) { let gui_elements = gui_elements_ref.borrow(); gui_elements.file_tab.on_browse_file(gui_elements_ref.clone(), move |gui_elements, main_window| { @@ -52,11 +54,14 @@ fn setup_file_tab(gui_elements_ref: Rc>) { if let Some(file_path) = file.to_str() { main_window.set_file_tab_browse_path(SharedString::from(file_path)); } - + + let file_tab = &gui_elements.file_tab; let file_name = if let Some(name) = file.file_name() {Some(name.to_string_lossy().to_string())} else {None}; - let (image, _info) = match load_image(file) { + + let (image, info) = match load_image(file) { Ok((image, info)) => (image, info), Err(error) => { + file_tab.clear_load(); show_error_message(error.to_string()); return; } @@ -64,14 +69,20 @@ fn setup_file_tab(gui_elements_ref: Rc>) { let img_size = image.size(); if img_size.width > VRAM_WIDTH as u32 || img_size.height > VRAM_HEIGHT as u32 { + file_tab.clear_load(); show_error_message(format!("Image size ({}; {}) is to big for VRAM ({}, {})", img_size.width, img_size.height, VRAM_WIDTH, VRAM_HEIGHT)); return; } - let file_tab = &mut gui_elements.file_tab; + logic_ref.borrow_mut().unadded_tim = info; file_tab.update_new_load(file_name, image); //main_window.invoke_change_to_main(); } }); + + gui_elements.file_tab.on_add_image(gui_elements_ref.clone(), move |gui_elements, _main_window| { + let file_tab = &gui_elements.file_tab; + file_tab.clear_load(); + }); } \ No newline at end of file diff --git a/src/Tools/tim_tool/src/tim_tool/mod.rs b/src/Tools/tim_tool/src/tim_tool/mod.rs deleted file mode 100644 index cce6c8ee..00000000 --- a/src/Tools/tim_tool/src/tim_tool/mod.rs +++ /dev/null @@ -1,8 +0,0 @@ -use std::path::PathBuf; -use tool_helper::Error; - -pub struct TIMImage {} - -pub fn load_image(path: PathBuf) -> Result<(slint::Image, TIMImage), Error> { - Ok((slint::Image::load_from_path(&path).or_else(|_| {Err(Error::from_str("Failed loading image"))})?, TIMImage{})) -} \ No newline at end of file diff --git a/src/Tools/tim_tool/ui/app-window.slint b/src/Tools/tim_tool/ui/app-window.slint index 45c315b1..a556f9dc 100644 --- a/src/Tools/tim_tool/ui/app-window.slint +++ b/src/Tools/tim_tool/ui/app-window.slint @@ -16,7 +16,9 @@ export component MainWindow inherits Window { in-out property file_tab_browse_path <=> file_tab.conv_image_path; in-out property file_tab_image_data <=> file_tab.conv_image_data; in-out property file_tab_image_name <=> file_tab.conv_image_name; + in-out property file_tab_enable <=> file_tab.conv_image_enable; callback file_tab_browse_convert_image <=> file_tab.conv_image_browse_clicked; + callback file_tab_add_convert_image <=> file_tab.conv_image_add_clicked; title: "TIM Tool 0.1.0"; width: tab_widget.width; diff --git a/src/Tools/tim_tool/ui/tab/file-tab.slint b/src/Tools/tim_tool/ui/tab/file-tab.slint index f5276b4e..58a39df2 100644 --- a/src/Tools/tim_tool/ui/tab/file-tab.slint +++ b/src/Tools/tim_tool/ui/tab/file-tab.slint @@ -15,8 +15,10 @@ component ConvertImageWidget inherits Rectangle { in-out property image_path; in-out property image_data; in-out property image_name; + in-out property enable_button: false; callback browse_clicked(); + callback add_clicked(); background: #D0D0D0; @@ -82,7 +84,8 @@ component ConvertImageWidget inherits Rectangle { padding: 4px; Button { text: "Add Image"; - enabled: false; + enabled: root.enable_button; + clicked => {root.add_clicked();} } } } @@ -94,8 +97,10 @@ export component FileTab inherits Rectangle { in-out property conv_image_path; in-out property conv_image_data; in-out property conv_image_name; + in-out property conv_image_enable; in-out property state; callback conv_image_browse_clicked; + callback conv_image_add_clicked; x: 0px; y: 0px; @@ -130,9 +135,15 @@ export component FileTab inherits Rectangle { image_path <=> root.conv_image_path; image_data <=> root.conv_image_data; image_name <=> root.conv_image_name; + enable_button <=> root.conv_image_enable; + browse_clicked => { root.conv_image_browse_clicked(); } + + add_clicked => { + root.conv_image_add_clicked(); + } } } } From 6fa574c921ee195b66a17455c0cebd5a6c94d582 Mon Sep 17 00:00:00 2001 From: Jaby Date: Sat, 15 Feb 2025 14:38:25 +0100 Subject: [PATCH 26/32] Add images --- src/Tools/tim_tool/src/gui/file_tab.rs | 4 +++ src/Tools/tim_tool/src/gui/main_tab.rs | 2 +- src/Tools/tim_tool/src/logic/mod.rs | 37 ++++++++++++++++++++++++-- src/Tools/tim_tool/src/logic/tim.rs | 6 +++++ src/Tools/tim_tool/src/main.rs | 14 +++++++--- 5 files changed, 56 insertions(+), 7 deletions(-) diff --git a/src/Tools/tim_tool/src/gui/file_tab.rs b/src/Tools/tim_tool/src/gui/file_tab.rs index fa889cbf..32c09761 100644 --- a/src/Tools/tim_tool/src/gui/file_tab.rs +++ b/src/Tools/tim_tool/src/gui/file_tab.rs @@ -34,6 +34,10 @@ impl FileTab { main_window.set_file_tab_enable(true); } + pub fn get_file_name(&self) -> String { + self.main_window.borrow().get_file_tab_image_name().to_string() + } + pub fn on_browse_file(&self, gui_elements: GUIElementsRef, mut function: impl FnMut(&mut GUIElements, &MainWindow) + 'static) { let main_window_cloned = self.main_window.clone(); let gui_cloned = gui_elements.clone(); diff --git a/src/Tools/tim_tool/src/gui/main_tab.rs b/src/Tools/tim_tool/src/gui/main_tab.rs index e0fa5b4b..86980bee 100644 --- a/src/Tools/tim_tool/src/gui/main_tab.rs +++ b/src/Tools/tim_tool/src/gui/main_tab.rs @@ -22,7 +22,7 @@ impl MainTab { MainTab{main_window, vram_file_list, vram_image_list} } - pub fn _add_new_vram_file(&mut self, file_name: String, image: slint::Image) { + pub fn add_new_vram_file(&mut self, file_name: &String, image: slint::Image) { let vram_image = VRAMImage{ img: image, x: 0, diff --git a/src/Tools/tim_tool/src/logic/mod.rs b/src/Tools/tim_tool/src/logic/mod.rs index 76929b11..d7acaf11 100644 --- a/src/Tools/tim_tool/src/logic/mod.rs +++ b/src/Tools/tim_tool/src/logic/mod.rs @@ -1,12 +1,45 @@ pub mod tim; +use slint::Image; use tim::TIMInfo; pub struct Logic { - pub unadded_tim: TIMInfo, + unadded_tim: UnaddedTIM, } impl Logic { pub fn new() -> Logic { - Logic{unadded_tim: TIMInfo{}} + Logic{unadded_tim: UnaddedTIM::empty()} + } + + pub fn set_unadded_tim(&mut self, info: TIMInfo, image: Image) { + self.unadded_tim = UnaddedTIM::new(info, image); + } + + pub fn add_unadded_tim_as(&mut self, _name: &String) -> Image { + let _info = self.unadded_tim.take_info(); + self.unadded_tim.take_image() + } +} + +struct UnaddedTIM { + info: TIMInfo, + image: Image, +} + +impl UnaddedTIM { + pub fn new(info: TIMInfo, image: Image) -> UnaddedTIM { + UnaddedTIM{info, image} + } + + pub fn take_info(&mut self) -> TIMInfo { + std::mem::take(&mut self.info) + } + + pub fn take_image(&mut self) -> Image { + std::mem::take(&mut self.image) + } + + fn empty() -> UnaddedTIM { + UnaddedTIM{info: TIMInfo{}, image: Image::default()} } } \ No newline at end of file diff --git a/src/Tools/tim_tool/src/logic/tim.rs b/src/Tools/tim_tool/src/logic/tim.rs index 735bb81e..e92945d5 100644 --- a/src/Tools/tim_tool/src/logic/tim.rs +++ b/src/Tools/tim_tool/src/logic/tim.rs @@ -3,6 +3,12 @@ use tool_helper::Error; pub struct TIMInfo {} +impl std::default::Default for TIMInfo { + fn default() -> Self { + TIMInfo{} + } +} + pub fn load_image(path: PathBuf) -> Result<(slint::Image, TIMInfo), Error> { Ok((slint::Image::load_from_path(&path).or_else(|_| {Err(Error::from_str("Failed loading image"))})?, TIMInfo{})) } \ No newline at end of file diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index 3f497eb6..60479f3b 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -40,6 +40,7 @@ fn setup_main_tab(gui_elements_ref: Rc>) { fn setup_file_tab(gui_elements_ref: Rc>, logic_ref: Rc>) { let gui_elements = gui_elements_ref.borrow(); + let logic = logic_ref.clone(); gui_elements.file_tab.on_browse_file(gui_elements_ref.clone(), move |gui_elements, main_window| { fn show_error_message(text: String) { MessageDialog::new().set_title("Loading Image failed").set_level(rfd::MessageLevel::Error).set_description(text).show(); @@ -74,15 +75,20 @@ fn setup_file_tab(gui_elements_ref: Rc>, logic_ref: Rc Date: Sat, 15 Feb 2025 17:07:41 +0100 Subject: [PATCH 27/32] Load indexed png --- src/Tools/tim_tool/Cargo.toml | 1 + src/Tools/tim_tool/src/logic/mod.rs | 2 +- src/Tools/tim_tool/src/logic/tim.rs | 59 ++++++++++++++++++++++++++--- src/Tools/tim_tool/src/main.rs | 6 +-- 4 files changed, 59 insertions(+), 9 deletions(-) diff --git a/src/Tools/tim_tool/Cargo.toml b/src/Tools/tim_tool/Cargo.toml index dd0b80c4..a5253132 100644 --- a/src/Tools/tim_tool/Cargo.toml +++ b/src/Tools/tim_tool/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" panic = "abort" [dependencies] +png = "0.17.16" rfd = "0.15.2" slint = "1.9.2" tiny-skia = "0.11.4" diff --git a/src/Tools/tim_tool/src/logic/mod.rs b/src/Tools/tim_tool/src/logic/mod.rs index d7acaf11..a6b0d99f 100644 --- a/src/Tools/tim_tool/src/logic/mod.rs +++ b/src/Tools/tim_tool/src/logic/mod.rs @@ -40,6 +40,6 @@ impl UnaddedTIM { } fn empty() -> UnaddedTIM { - UnaddedTIM{info: TIMInfo{}, image: Image::default()} + UnaddedTIM{info: TIMInfo::default(), image: Image::default()} } } \ No newline at end of file diff --git a/src/Tools/tim_tool/src/logic/tim.rs b/src/Tools/tim_tool/src/logic/tim.rs index e92945d5..5487e39e 100644 --- a/src/Tools/tim_tool/src/logic/tim.rs +++ b/src/Tools/tim_tool/src/logic/tim.rs @@ -1,14 +1,63 @@ -use std::path::PathBuf; +use std::{default, fmt::format, fs::File, path::PathBuf}; +use slint::{Rgba8Pixel, SharedPixelBuffer}; use tool_helper::Error; -pub struct TIMInfo {} +pub struct TIMInfo { + image_data: SharedPixelBuffer, + palette: Option>, +} impl std::default::Default for TIMInfo { fn default() -> Self { - TIMInfo{} + TIMInfo{image_data: SharedPixelBuffer::new(1, 1), palette: None} } } -pub fn load_image(path: PathBuf) -> Result<(slint::Image, TIMInfo), Error> { - Ok((slint::Image::load_from_path(&path).or_else(|_| {Err(Error::from_str("Failed loading image"))})?, TIMInfo{})) +pub fn load_image(path: &PathBuf) -> Result<(slint::Image, TIMInfo), Error> { + fn make_color(iter: &mut dyn std::iter::Iterator) -> Option { + Some(Rgba8Pixel::new( + *iter.next()?, *iter.next()?, *iter.next()?, + 0xFF)) + } + + let mut reader = png::Decoder::new(File::open(path)?).read_info().or_else(|error| {Err(Error::from_error(error))})?; + let info = reader.info().clone(); + + let mut buffer = vec![0; reader.output_buffer_size()]; + let frame_info = reader.next_frame(&mut buffer).or_else(|error| {Err(Error::from_error(error))})?; + let bit_depth = frame_info.bit_depth; + let mut image_data = SharedPixelBuffer::new(frame_info.width, frame_info.height); + + if info.color_type == png::ColorType::Indexed { + let palette = info.palette.ok_or(Error::from_str("Found indexed PNG without palette"))?; + let mut palette_colors = Vec::new(); + let mut iter = palette.iter(); + + while let Some(color) = make_color(&mut iter) { + palette_colors.push(color); + } + + let mut dst_pixels = image_data.make_mut_slice(); + for byte in buffer.into_iter() { + match bit_depth { + png::BitDepth::Four => { + dst_pixels[0] = palette_colors[(byte >> 4) as usize]; + dst_pixels[1] = palette_colors[(byte & 0xF) as usize]; + dst_pixels = &mut dst_pixels[2..]; + }, + png::BitDepth::Eight => { + dst_pixels[0] = palette_colors[byte as usize]; + dst_pixels = &mut dst_pixels[1..]; + }, + _ => {return Err(Error::from_str("Only 4 and 8bit color depth are supported"));} + } + } + + let image = slint::Image::from_rgba8_premultiplied(image_data.clone()); + Ok((image, TIMInfo{image_data, palette: Some(palette_colors)})) + } + + else { + Err(Error::not_implemented("Support for non indexed images")) + } } \ No newline at end of file diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index 60479f3b..aa2c5882 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -47,8 +47,8 @@ fn setup_file_tab(gui_elements_ref: Rc>, logic_ref: Rc>, logic_ref: Rc (image, info), Err(error) => { file_tab.clear_load(); From 2398053c61edf60bc2f0e5f456709ff993087712 Mon Sep 17 00:00:00 2001 From: Jaby Date: Sat, 15 Feb 2025 17:33:34 +0100 Subject: [PATCH 28/32] Further improvements --- src/Tools/tim_tool/src/gui/file_tab.rs | 15 ++-- src/Tools/tim_tool/src/gui/mod.rs | 5 ++ src/Tools/tim_tool/src/logic/mod.rs | 48 ++++++------- src/Tools/tim_tool/src/logic/tim.rs | 95 +++++++++++++------------- src/Tools/tim_tool/src/main.rs | 31 +++------ 5 files changed, 92 insertions(+), 102 deletions(-) diff --git a/src/Tools/tim_tool/src/gui/file_tab.rs b/src/Tools/tim_tool/src/gui/file_tab.rs index 32c09761..02b1c9c3 100644 --- a/src/Tools/tim_tool/src/gui/file_tab.rs +++ b/src/Tools/tim_tool/src/gui/file_tab.rs @@ -1,6 +1,7 @@ use crate::MainWindow; -use super::{GUIElements, GUIElementsRef, MainWindowRef}; +use super::{GUIElements, GUIElementsRef, MainWindowRef, display_error}; use slint::Image; +use tool_helper::Error; pub struct FileTab { main_window: MainWindowRef @@ -38,21 +39,25 @@ impl FileTab { self.main_window.borrow().get_file_tab_image_name().to_string() } - pub fn on_browse_file(&self, gui_elements: GUIElementsRef, mut function: impl FnMut(&mut GUIElements, &MainWindow) + 'static) { + pub fn on_browse_file(&self, gui_elements: GUIElementsRef, mut function: impl FnMut(&mut GUIElements, &MainWindow) -> Result<(), Error> + 'static) { let main_window_cloned = self.main_window.clone(); let gui_cloned = gui_elements.clone(); self.main_window.borrow().on_file_tab_browse_convert_image(move || { - function(&mut gui_cloned.borrow_mut(), &main_window_cloned.borrow()); + if let Err(error) = function(&mut gui_cloned.borrow_mut(), &main_window_cloned.borrow()) { + display_error("Loadind file failed", &error.to_string()); + } }); } - pub fn on_add_image(&self, gui_elements: GUIElementsRef, mut function: impl FnMut(&mut GUIElements, &MainWindow) + 'static) { + pub fn on_add_image(&self, gui_elements: GUIElementsRef, mut function: impl FnMut(&mut GUIElements, &MainWindow) -> Result<(), Error> + 'static) { let main_window_cloned = self.main_window.clone(); let gui_cloned = gui_elements.clone(); self.main_window.borrow().on_file_tab_add_convert_image(move || { - function(&mut gui_cloned.borrow_mut(), &main_window_cloned.borrow()); + if let Err(error) = function(&mut gui_cloned.borrow_mut(), &main_window_cloned.borrow()) { + display_error("Adding file failed", &error.to_string()); + } }); } } \ No newline at end of file diff --git a/src/Tools/tim_tool/src/gui/mod.rs b/src/Tools/tim_tool/src/gui/mod.rs index 163db6ce..5be11d69 100644 --- a/src/Tools/tim_tool/src/gui/mod.rs +++ b/src/Tools/tim_tool/src/gui/mod.rs @@ -3,6 +3,7 @@ mod gui_elements; mod main_tab; use crate::MainWindow; +use rfd::MessageDialog; use std::{cell::RefCell, rc::Rc}; use slint::{Rgba8Pixel, SharedPixelBuffer}; use tiny_skia::{Rect, Transform}; @@ -15,6 +16,10 @@ pub const VRAM_HEIGHT:usize = 512; type MainWindowRef = Rc>; type GUIElementsRef = Rc>; +fn display_error(title: &str, text: &String) { + MessageDialog::new().set_title(title).set_level(rfd::MessageLevel::Error).set_description(text).show(); +} + fn create_vram_bg(rect_width: u32, rect_height: u32) -> Option> { let mut pixel_buffer = SharedPixelBuffer::::new(VRAM_WIDTH as u32, VRAM_HEIGHT as u32); let width = pixel_buffer.width(); diff --git a/src/Tools/tim_tool/src/logic/mod.rs b/src/Tools/tim_tool/src/logic/mod.rs index a6b0d99f..aba1c083 100644 --- a/src/Tools/tim_tool/src/logic/mod.rs +++ b/src/Tools/tim_tool/src/logic/mod.rs @@ -1,45 +1,37 @@ pub mod tim; + +use std::path::PathBuf; use slint::Image; use tim::TIMInfo; +use tool_helper::Error; pub struct Logic { - unadded_tim: UnaddedTIM, + unadded_tim: Option, } impl Logic { pub fn new() -> Logic { - Logic{unadded_tim: UnaddedTIM::empty()} + Logic{unadded_tim: None} } - pub fn set_unadded_tim(&mut self, info: TIMInfo, image: Image) { - self.unadded_tim = UnaddedTIM::new(info, image); + pub fn set_unadded_tim(&mut self, path: &PathBuf) -> Result { + let tim_info = TIMInfo::from_image(path)?; + let image = tim_info.get_slint_images(); + + self.unadded_tim = Some(tim_info); + Ok(image) } - pub fn add_unadded_tim_as(&mut self, _name: &String) -> Image { - let _info = self.unadded_tim.take_info(); - self.unadded_tim.take_image() - } -} + pub fn add_unadded_tim_as(&mut self, _name: &String) -> Result { + if let Some(unadded_tim) = &self.unadded_tim { + let image = unadded_tim.get_slint_images(); -struct UnaddedTIM { - info: TIMInfo, - image: Image, -} + self.unadded_tim = None; + Ok(image) + } -impl UnaddedTIM { - pub fn new(info: TIMInfo, image: Image) -> UnaddedTIM { - UnaddedTIM{info, image} - } - - pub fn take_info(&mut self) -> TIMInfo { - std::mem::take(&mut self.info) - } - - pub fn take_image(&mut self) -> Image { - std::mem::take(&mut self.image) - } - - fn empty() -> UnaddedTIM { - UnaddedTIM{info: TIMInfo::default(), image: Image::default()} + else { + Err(Error::from_str("No data found for loaded image")) + } } } \ No newline at end of file diff --git a/src/Tools/tim_tool/src/logic/tim.rs b/src/Tools/tim_tool/src/logic/tim.rs index 5487e39e..bfa96882 100644 --- a/src/Tools/tim_tool/src/logic/tim.rs +++ b/src/Tools/tim_tool/src/logic/tim.rs @@ -1,4 +1,4 @@ -use std::{default, fmt::format, fs::File, path::PathBuf}; +use std::{fs::File, path::PathBuf}; use slint::{Rgba8Pixel, SharedPixelBuffer}; use tool_helper::Error; @@ -7,57 +7,56 @@ pub struct TIMInfo { palette: Option>, } -impl std::default::Default for TIMInfo { - fn default() -> Self { - TIMInfo{image_data: SharedPixelBuffer::new(1, 1), palette: None} - } -} - -pub fn load_image(path: &PathBuf) -> Result<(slint::Image, TIMInfo), Error> { - fn make_color(iter: &mut dyn std::iter::Iterator) -> Option { - Some(Rgba8Pixel::new( - *iter.next()?, *iter.next()?, *iter.next()?, - 0xFF)) - } - - let mut reader = png::Decoder::new(File::open(path)?).read_info().or_else(|error| {Err(Error::from_error(error))})?; - let info = reader.info().clone(); +impl TIMInfo { + pub fn from_image(path: &PathBuf) -> Result { + fn make_color(iter: &mut dyn std::iter::Iterator) -> Option { + Some(Rgba8Pixel::new( + *iter.next()?, *iter.next()?, *iter.next()?, + 0xFF)) + } - let mut buffer = vec![0; reader.output_buffer_size()]; - let frame_info = reader.next_frame(&mut buffer).or_else(|error| {Err(Error::from_error(error))})?; - let bit_depth = frame_info.bit_depth; - let mut image_data = SharedPixelBuffer::new(frame_info.width, frame_info.height); - - if info.color_type == png::ColorType::Indexed { - let palette = info.palette.ok_or(Error::from_str("Found indexed PNG without palette"))?; - let mut palette_colors = Vec::new(); - let mut iter = palette.iter(); - - while let Some(color) = make_color(&mut iter) { - palette_colors.push(color); - } - - let mut dst_pixels = image_data.make_mut_slice(); - for byte in buffer.into_iter() { - match bit_depth { - png::BitDepth::Four => { - dst_pixels[0] = palette_colors[(byte >> 4) as usize]; - dst_pixels[1] = palette_colors[(byte & 0xF) as usize]; - dst_pixels = &mut dst_pixels[2..]; - }, - png::BitDepth::Eight => { - dst_pixels[0] = palette_colors[byte as usize]; - dst_pixels = &mut dst_pixels[1..]; - }, - _ => {return Err(Error::from_str("Only 4 and 8bit color depth are supported"));} + let mut reader = png::Decoder::new(File::open(path)?).read_info().or_else(|error| {Err(Error::from_error(error))})?; + let info = reader.info().clone(); + + let mut buffer = vec![0; reader.output_buffer_size()]; + let frame_info = reader.next_frame(&mut buffer).or_else(|error| {Err(Error::from_error(error))})?; + let bit_depth = frame_info.bit_depth; + let mut image_data = SharedPixelBuffer::new(frame_info.width, frame_info.height); + + if info.color_type == png::ColorType::Indexed { + let palette = info.palette.ok_or(Error::from_str("Found indexed PNG without palette"))?; + let mut palette_colors = Vec::new(); + let mut iter = palette.iter(); + + while let Some(color) = make_color(&mut iter) { + palette_colors.push(color); + } + + let mut dst_pixels = image_data.make_mut_slice(); + for byte in buffer.into_iter() { + match bit_depth { + png::BitDepth::Four => { + dst_pixels[0] = palette_colors[(byte >> 4) as usize]; + dst_pixels[1] = palette_colors[(byte & 0xF) as usize]; + dst_pixels = &mut dst_pixels[2..]; + }, + png::BitDepth::Eight => { + dst_pixels[0] = palette_colors[byte as usize]; + dst_pixels = &mut dst_pixels[1..]; + }, + _ => {return Err(Error::from_str("Only 4 and 8bit color depth are supported"));} + } } - } - let image = slint::Image::from_rgba8_premultiplied(image_data.clone()); - Ok((image, TIMInfo{image_data, palette: Some(palette_colors)})) + Ok(TIMInfo{image_data, palette: Some(palette_colors)}) + } + + else { + Err(Error::not_implemented("Support for non indexed images")) + } } - else { - Err(Error::not_implemented("Support for non indexed images")) + pub fn get_slint_images(&self) -> slint::Image { + slint::Image::from_rgba8_premultiplied(self.image_data.clone()) } } \ No newline at end of file diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index aa2c5882..086a6cbf 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -3,10 +3,11 @@ mod gui; use gui::{GUIElements, VRAM_WIDTH, VRAM_HEIGHT}; -use rfd::{FileDialog, MessageDialog}; +use rfd::FileDialog; use std::{cell::RefCell, rc::Rc}; use slint::SharedString; -use tim_tool::logic::{tim::load_image, Logic}; +use tim_tool::logic::Logic; +use tool_helper::Error; slint::include_modules!(); @@ -41,11 +42,7 @@ fn setup_file_tab(gui_elements_ref: Rc>, logic_ref: Rc Result<(), Error> { let file = FileDialog::new() .add_filter("PNG image (.png)", &["png"]) .set_title("PNG image file") @@ -59,25 +56,16 @@ fn setup_file_tab(gui_elements_ref: Rc>, logic_ref: Rc (image, info), - Err(error) => { - file_tab.clear_load(); - show_error_message(error.to_string()); - return; - } - }; + let image = logic.borrow_mut().set_unadded_tim(&file)?; let img_size = image.size(); if img_size.width > VRAM_WIDTH as u32 || img_size.height > VRAM_HEIGHT as u32 { - file_tab.clear_load(); - show_error_message(format!("Image size ({}; {}) is to big for VRAM ({}, {})", img_size.width, img_size.height, VRAM_WIDTH, VRAM_HEIGHT)); - return; + return Err(Error::from_text(format!("Image size ({}; {}) is to big for VRAM ({}, {})", img_size.width, img_size.height, VRAM_WIDTH, VRAM_HEIGHT))); } - - logic.borrow_mut().set_unadded_tim(info, image.clone()); file_tab.update_new_load(file_name, image); } + + Ok(()) }); let logic = logic_ref.clone(); @@ -86,9 +74,10 @@ fn setup_file_tab(gui_elements_ref: Rc>, logic_ref: Rc Date: Sun, 16 Feb 2025 15:27:07 +0100 Subject: [PATCH 29/32] Support direct color PNG images --- src/Tools/tim_tool/src/logic/tim.rs | 40 +++++++++++++++++++---------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/Tools/tim_tool/src/logic/tim.rs b/src/Tools/tim_tool/src/logic/tim.rs index bfa96882..cfdb26c3 100644 --- a/src/Tools/tim_tool/src/logic/tim.rs +++ b/src/Tools/tim_tool/src/logic/tim.rs @@ -9,30 +9,35 @@ pub struct TIMInfo { impl TIMInfo { pub fn from_image(path: &PathBuf) -> Result { - fn make_color(iter: &mut dyn std::iter::Iterator) -> Option { + fn make_color(iter: &mut dyn std::iter::Iterator, load_alpha: bool) -> Option { Some(Rgba8Pixel::new( *iter.next()?, *iter.next()?, *iter.next()?, - 0xFF)) + if load_alpha {*iter.next()?} else {0xFF})) } let mut reader = png::Decoder::new(File::open(path)?).read_info().or_else(|error| {Err(Error::from_error(error))})?; let info = reader.info().clone(); - let mut buffer = vec![0; reader.output_buffer_size()]; - let frame_info = reader.next_frame(&mut buffer).or_else(|error| {Err(Error::from_error(error))})?; - let bit_depth = frame_info.bit_depth; - let mut image_data = SharedPixelBuffer::new(frame_info.width, frame_info.height); - + let mut buffer = vec![0; reader.output_buffer_size()]; + let frame_info = reader.next_frame(&mut buffer).or_else(|error| {Err(Error::from_error(error))})?; + let bytes_per_pixel = info.bytes_per_pixel(); + let bit_depth = frame_info.bit_depth; + let mut image_data = SharedPixelBuffer::new(frame_info.width, frame_info.height); + let mut dst_pixels = image_data.make_mut_slice(); + + if bytes_per_pixel != 3 && bytes_per_pixel != 4 { + return Err(Error::from_text(format!("Image has {} bytes per pixel, but only 3 and 4 bytes per pixel are supported", bytes_per_pixel))); + } + if info.color_type == png::ColorType::Indexed { let palette = info.palette.ok_or(Error::from_str("Found indexed PNG without palette"))?; let mut palette_colors = Vec::new(); let mut iter = palette.iter(); - while let Some(color) = make_color(&mut iter) { + while let Some(color) = make_color(&mut iter, false) { palette_colors.push(color); } - - let mut dst_pixels = image_data.make_mut_slice(); + for byte in buffer.into_iter() { match bit_depth { png::BitDepth::Four => { @@ -44,15 +49,24 @@ impl TIMInfo { dst_pixels[0] = palette_colors[byte as usize]; dst_pixels = &mut dst_pixels[1..]; }, - _ => {return Err(Error::from_str("Only 4 and 8bit color depth are supported"));} + _ => {return Err(Error::from_str("Only 4 and 8bit color depth are supported for indexed color images"));} } } - Ok(TIMInfo{image_data, palette: Some(palette_colors)}) } else { - Err(Error::not_implemented("Support for non indexed images")) + let mut byte_iter = buffer.iter(); + while let Some(color) = make_color(&mut byte_iter, bytes_per_pixel == 4) { + match bit_depth { + png::BitDepth::Eight => { + dst_pixels[0] = color; + dst_pixels = &mut dst_pixels[1..]; + } + _ => {return Err(Error::from_str("Only 8bit color depth are supported for direct color images"));} + } + } + Ok(TIMInfo{image_data, palette: None}) } } From ac6a46134e0a289dffe4944567df77f7c576c27d Mon Sep 17 00:00:00 2001 From: Jaby Date: Sun, 16 Feb 2025 15:37:00 +0100 Subject: [PATCH 30/32] Fix for indexed images not loading anymore --- src/Tools/tim_tool/src/logic/tim.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Tools/tim_tool/src/logic/tim.rs b/src/Tools/tim_tool/src/logic/tim.rs index cfdb26c3..64ea2f1e 100644 --- a/src/Tools/tim_tool/src/logic/tim.rs +++ b/src/Tools/tim_tool/src/logic/tim.rs @@ -25,10 +25,6 @@ impl TIMInfo { let mut image_data = SharedPixelBuffer::new(frame_info.width, frame_info.height); let mut dst_pixels = image_data.make_mut_slice(); - if bytes_per_pixel != 3 && bytes_per_pixel != 4 { - return Err(Error::from_text(format!("Image has {} bytes per pixel, but only 3 and 4 bytes per pixel are supported", bytes_per_pixel))); - } - if info.color_type == png::ColorType::Indexed { let palette = info.palette.ok_or(Error::from_str("Found indexed PNG without palette"))?; let mut palette_colors = Vec::new(); @@ -54,8 +50,12 @@ impl TIMInfo { } Ok(TIMInfo{image_data, palette: Some(palette_colors)}) } - + else { + if bytes_per_pixel != 3 && bytes_per_pixel != 4 { + return Err(Error::from_text(format!("Image has {} bytes per pixel, but only 3 and 4 bytes per pixel are supported", bytes_per_pixel))); + } + let mut byte_iter = buffer.iter(); while let Some(color) = make_color(&mut byte_iter, bytes_per_pixel == 4) { match bit_depth { From 6ddca9e79f5cd1b258a3258d29400a5e0258ca54 Mon Sep 17 00:00:00 2001 From: Jaby Date: Sun, 16 Feb 2025 15:49:25 +0100 Subject: [PATCH 31/32] Add palette flag that prevents deleting --- src/Tools/tim_tool/src/gui/main_tab.rs | 16 ++++++++++++---- src/Tools/tim_tool/src/gui/mod.rs | 6 +++++- src/Tools/tim_tool/src/main.rs | 6 ++++-- src/Tools/tim_tool/ui/tab/main-tab.slint | 7 ++++--- 4 files changed, 25 insertions(+), 10 deletions(-) diff --git a/src/Tools/tim_tool/src/gui/main_tab.rs b/src/Tools/tim_tool/src/gui/main_tab.rs index 86980bee..61d5609e 100644 --- a/src/Tools/tim_tool/src/gui/main_tab.rs +++ b/src/Tools/tim_tool/src/gui/main_tab.rs @@ -24,18 +24,26 @@ impl MainTab { pub fn add_new_vram_file(&mut self, file_name: &String, image: slint::Image) { let vram_image = VRAMImage{ - img: image, - x: 0, - y: 0 + img: image, + x: 0, + y: 0, + is_palette: false, }; self.vram_file_list.push(slint::StandardListViewItem::from(file_name.as_str())); self.vram_image_list.push(vram_image); } - pub fn remove_vram_file(&mut self, idx: usize) { + pub fn remove_vram_file(&mut self, idx: usize) -> bool { + if let Some(element) = self.vram_image_list.iter().skip(idx).next() { + if element.is_palette { + return false; + } + } + self.vram_file_list.remove(idx); self.vram_image_list.remove(idx); + return true; } pub fn move_vram_image(&mut self, idx: usize, dx: i32, dy: i32) { diff --git a/src/Tools/tim_tool/src/gui/mod.rs b/src/Tools/tim_tool/src/gui/mod.rs index 5be11d69..438d75fd 100644 --- a/src/Tools/tim_tool/src/gui/mod.rs +++ b/src/Tools/tim_tool/src/gui/mod.rs @@ -16,7 +16,11 @@ pub const VRAM_HEIGHT:usize = 512; type MainWindowRef = Rc>; type GUIElementsRef = Rc>; -fn display_error(title: &str, text: &String) { +pub fn display_information(title: impl Into, text: impl Into) { + MessageDialog::new().set_title(title).set_level(rfd::MessageLevel::Info).set_description(text).show(); +} + +pub fn display_error(title: impl Into, text: impl Into) { MessageDialog::new().set_title(title).set_level(rfd::MessageLevel::Error).set_description(text).show(); } diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index 086a6cbf..f89dafd9 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -2,7 +2,7 @@ #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] mod gui; -use gui::{GUIElements, VRAM_WIDTH, VRAM_HEIGHT}; +use gui::{GUIElements, VRAM_WIDTH, VRAM_HEIGHT, display_information}; use rfd::FileDialog; use std::{cell::RefCell, rc::Rc}; use slint::SharedString; @@ -33,7 +33,9 @@ fn setup_main_tab(gui_elements_ref: Rc>) { gui_elements.main_tab.on_remove_file(gui_elements_ref.clone(), move |main_tab, _main_window, idx| { if idx >= 0 { - main_tab.remove_vram_file(idx as usize); + if !main_tab.remove_vram_file(idx as usize) { + display_information("Removing VRAM file", "Can not remove palette. Delete image instead"); + } } }); } diff --git a/src/Tools/tim_tool/ui/tab/main-tab.slint b/src/Tools/tim_tool/ui/tab/main-tab.slint index ae73d468..028b333e 100644 --- a/src/Tools/tim_tool/ui/tab/main-tab.slint +++ b/src/Tools/tim_tool/ui/tab/main-tab.slint @@ -2,9 +2,10 @@ import { VRAMArea } from "../vram-components.slint"; import { Button, ComboBox, GroupBox, StandardListView } from "std-widgets.slint"; struct VRAMImage { - img: image, - x: int, - y: int, + img: image, + x: int, + y: int, + is_palette: bool, } export component MainTab inherits Rectangle { From 2af28b72c88e6af8a045f38d9d8a52f262654c8a Mon Sep 17 00:00:00 2001 From: Jaby Date: Sun, 16 Feb 2025 16:11:40 +0100 Subject: [PATCH 32/32] Prepare for palette --- src/Tools/tim_tool/src/gui/main_tab.rs | 29 ++++++++++++++++-------- src/Tools/tim_tool/src/logic/mod.rs | 4 ++-- src/Tools/tim_tool/src/logic/tim.rs | 4 ++-- src/Tools/tim_tool/src/main.rs | 9 ++++---- src/Tools/tim_tool/ui/tab/main-tab.slint | 9 ++++---- 5 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/Tools/tim_tool/src/gui/main_tab.rs b/src/Tools/tim_tool/src/gui/main_tab.rs index 61d5609e..3647dfe3 100644 --- a/src/Tools/tim_tool/src/gui/main_tab.rs +++ b/src/Tools/tim_tool/src/gui/main_tab.rs @@ -2,10 +2,11 @@ use crate::{gui::{VRAM_HEIGHT, VRAM_WIDTH}, MainWindow, VRAMImage}; use super::{GUIElementsRef, MainWindowRef}; use slint::Model; -use std::rc::Rc; +use std::{rc::Rc, sync::{Arc, Mutex}}; pub struct MainTab { main_window: MainWindowRef, + mtx: Arc>, vram_file_list: Rc>, vram_image_list: Rc>, } @@ -19,22 +20,30 @@ impl MainTab { main_window.borrow().set_main_tab_vram_file_list(vram_file_list.clone().into()); main_window.borrow().set_main_tab_vram_images(vram_image_list.clone().into()); - MainTab{main_window, vram_file_list, vram_image_list} + MainTab{main_window, mtx: Arc::new(Mutex::new(0)), vram_file_list, vram_image_list} } - pub fn add_new_vram_file(&mut self, file_name: &String, image: slint::Image) { - let vram_image = VRAMImage{ - img: image, - x: 0, - y: 0, - is_palette: false, + pub fn add_new_vram_file(&mut self, file_name: &String, image: slint::Image, image_palette: Option) { + let add_new_image = |file_name: &String, image: slint::Image| { + let vram_image = VRAMImage{ + img: image, + x: 0, + y: 0, + palette_count: 0, + is_palette: false, + }; + + self.vram_file_list.push(slint::StandardListViewItem::from(file_name.as_str())); + self.vram_image_list.push(vram_image); }; - self.vram_file_list.push(slint::StandardListViewItem::from(file_name.as_str())); - self.vram_image_list.push(vram_image); + let _lock = self.mtx.lock().unwrap(); + add_new_image(file_name, image); } pub fn remove_vram_file(&mut self, idx: usize) -> bool { + let _lock = self.mtx.lock().unwrap(); + if let Some(element) = self.vram_image_list.iter().skip(idx).next() { if element.is_palette { return false; diff --git a/src/Tools/tim_tool/src/logic/mod.rs b/src/Tools/tim_tool/src/logic/mod.rs index aba1c083..c938513f 100644 --- a/src/Tools/tim_tool/src/logic/mod.rs +++ b/src/Tools/tim_tool/src/logic/mod.rs @@ -14,7 +14,7 @@ impl Logic { Logic{unadded_tim: None} } - pub fn set_unadded_tim(&mut self, path: &PathBuf) -> Result { + pub fn set_unadded_tim(&mut self, path: &PathBuf) -> Result<(Image, Option), Error> { let tim_info = TIMInfo::from_image(path)?; let image = tim_info.get_slint_images(); @@ -22,7 +22,7 @@ impl Logic { Ok(image) } - pub fn add_unadded_tim_as(&mut self, _name: &String) -> Result { + pub fn add_unadded_tim_as(&mut self, _name: &String) -> Result<(Image, Option), Error> { if let Some(unadded_tim) = &self.unadded_tim { let image = unadded_tim.get_slint_images(); diff --git a/src/Tools/tim_tool/src/logic/tim.rs b/src/Tools/tim_tool/src/logic/tim.rs index 64ea2f1e..025eed4e 100644 --- a/src/Tools/tim_tool/src/logic/tim.rs +++ b/src/Tools/tim_tool/src/logic/tim.rs @@ -70,7 +70,7 @@ impl TIMInfo { } } - pub fn get_slint_images(&self) -> slint::Image { - slint::Image::from_rgba8_premultiplied(self.image_data.clone()) + pub fn get_slint_images(&self) -> (slint::Image, Option) { + (slint::Image::from_rgba8_premultiplied(self.image_data.clone()), None) } } \ No newline at end of file diff --git a/src/Tools/tim_tool/src/main.rs b/src/Tools/tim_tool/src/main.rs index f89dafd9..3506d5a5 100644 --- a/src/Tools/tim_tool/src/main.rs +++ b/src/Tools/tim_tool/src/main.rs @@ -58,7 +58,7 @@ fn setup_file_tab(gui_elements_ref: Rc>, logic_ref: Rc VRAM_WIDTH as u32 || img_size.height > VRAM_HEIGHT as u32 { @@ -75,9 +75,10 @@ fn setup_file_tab(gui_elements_ref: Rc>, logic_ref: Rc