Compare commits

...

7 Commits

Author SHA1 Message Date
Jaby a62728db7b Crappy scale slider implementation 2025-03-02 00:45:23 +01:00
Jaby 39dace6f6b Fix layout scaling issue 2025-03-02 00:09:11 +01:00
Jaby e25cc7ba38 Make test scale work? 2025-02-23 07:38:11 +01:00
Jaby a797bff7a1 Simple scale concept 2025-02-17 17:46:59 +01:00
Jaby ebf640520e Support movement by key 2025-02-17 17:05:24 +01:00
Jaby b4c6cb70b6 Make image pos editable 2025-02-17 16:53:35 +01:00
Jaby fb1ecf17c5 Load palette 2025-02-17 11:29:47 +01:00
4 changed files with 202 additions and 86 deletions

View File

@ -43,7 +43,7 @@ impl MainTab {
add_new_image(file_name, image, if image_palette.is_some() {1} else {0}, false); add_new_image(file_name, image, if image_palette.is_some() {1} else {0}, false);
if let Some(image_palette) = image_palette { if let Some(image_palette) = image_palette {
let file_name = file_name.clone() + " (Palette)"; let file_name = " => ".to_owned() + file_name.as_str() + " (Palette)";
add_new_image(&file_name, image_palette, 0, true); add_new_image(&file_name, image_palette, 0, true);
} }
} }

View File

@ -71,6 +71,22 @@ impl TIMInfo {
} }
pub fn get_slint_images(&self) -> (slint::Image, Option<slint::Image>) { pub fn get_slint_images(&self) -> (slint::Image, Option<slint::Image>) {
(slint::Image::from_rgba8_premultiplied(self.image_data.clone()), None) (slint::Image::from_rgba8_premultiplied(self.image_data.clone()), if let Some(palette) = &self.palette {
let width = if palette.len() <= 16 {16} else {256};
Some(Self::get_palette_image(palette, width, 1))
} else {
None
})
}
fn get_palette_image(palette: &Vec<Rgba8Pixel>, width: u32, height: u32) -> slint::Image {
let mut image_data = SharedPixelBuffer::new(width, height);
let dst_pixels = image_data.make_mut_slice();
for (idx, byte) in dst_pixels.iter_mut().enumerate() {
*byte = if idx < palette.len() {palette[idx]} else {Rgba8Pixel::new(0, 0, 0, 0xFF)};
}
slint::Image::from_rgba8_premultiplied(image_data.clone())
} }
} }

View File

@ -1,5 +1,5 @@
import { VRAMArea } from "../vram-components.slint"; import { VRAMArea } from "../vram-components.slint";
import { Button, ComboBox, GroupBox, StandardListView } from "std-widgets.slint"; import { Button, ComboBox, GroupBox, StandardListView, LineEdit, ScrollView, Slider } from "std-widgets.slint";
struct VRAMImage { struct VRAMImage {
img: image, img: image,
@ -10,6 +10,7 @@ struct VRAMImage {
} }
export component MainTab inherits Rectangle { export component MainTab inherits Rectangle {
property <int> test_scale: 1;
in-out property <image> vram_bg; in-out property <image> vram_bg;
in-out property <[StandardListViewItem]> vram_files: []; in-out property <[StandardListViewItem]> vram_files: [];
in-out property <[VRAMImage]> vram_images: []; in-out property <[VRAMImage]> vram_images: [];
@ -23,72 +24,105 @@ export component MainTab inherits Rectangle {
group := GroupBox { group := GroupBox {
title: "VRAM Layout"; title: "VRAM Layout";
x: 4px; x: 4px;
y: 4px; y: 4px;
width: main_view.width + 2*main_view.x;
VerticalLayout { VerticalLayout {
background_rect := Rectangle { main_view := ScrollView {
width: background_image.width + root.get_border_width()*2px; width: rect.width/root.test_scale;
height: background_image.height + root.get_border_width()*2px; height: rect.height/root.test_scale;
border-width: root.get_border_width()*1px; viewport-x: 0;
border-color: #404040; viewport-y: 0;
background: #A0A0A0; viewport-width: rect.width;
background_image := VRAMArea { viewport-height: rect.height;
x: root.get_border_width()*1px;
y: root.get_border_width()*1px; rect := Rectangle {
img: vram_bg; width: background_image.width + root.get_border_width()*2px;
img_x: 0; height: background_image.height + root.get_border_width()*2px;
img_y: 0; border-width: root.get_border_width()*1px;
border-color: #404040;
background: #A0A0A0;
background_image := VRAMArea {
x: root.get_border_width()*1px;
y: root.get_border_width()*1px;
img: vram_bg;
img_x: 0;
img_y: 0;
scale: test_scale;
}
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;
scale: test_scale;
TouchArea {
x: ((parent.img_x + self.lines_crossed_x())*1px)*test_scale;
y: ((parent.img_y + self.lines_crossed_y())*1px)*test_scale;
width: ((parent.img.width + self.lines_crossed_width())*1px)*test_scale;
height: ((parent.img.height + self.lines_crossed_height())*1px)*test_scale;
mouse-cursor: grab;
moved => {
self.mouse-cursor = MouseCursor.grabbing;
root.move_vram_image(i, ((self.mouse-x - self.pressed-x)/test_scale)/1px, ((self.mouse-y - self.pressed-y)/test_scale)/1px);
cur_sel_x.text = parent.img_x;
cur_sel_y.text = parent.img_y;
}
pointer-event(event) => {
if event.kind == PointerEventKind.up {
self.mouse-cursor = MouseCursor.grab;
}
if event.kind == PointerEventKind.down {
cur_sel_x.text = parent.img_x;
cur_sel_y.text = parent.img_y;
cur_sel_img.source = parent.img;
cur_sel_img.visible = true;
vram_files_list.current-item = i;
}
}
// 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 {
padding: 4px;
VerticalLayout {
alignment: center;
padding: 4px;
Text {
text: "Scale: " + slider.value;
}
}
slider := Slider {
minimum: 100.0;
maximum: 400.0;
step: 1.0;
value: 100.0;
for vram_image[i] in root.vram_images: VRAMArea { changed(value) => {
x: root.get_border_width()*1px; root.test_scale = value/100.0;
y: root.get_border_width()*1px;
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);
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;
cur_sel_img.source = parent.img;
cur_sel_img.visible = true;
vram_files_list.current-item = i;
}
}
// 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();
}
} }
} }
} }
@ -100,15 +134,15 @@ export component MainTab inherits Rectangle {
alignment: start; alignment: start;
padding: 4px; padding: 4px;
vram_files_list := StandardListView { vram_files_list := StandardListView {
width: background_image.width/2; width: background_image.width/root.test_scale/2;
height: 128px; height: 128px;
model: root.vram_files; model: root.vram_files;
current-item-changed(current-item) => { current-item-changed(current-item) => {
cur_sel_x.display_value = root.vram_images[current-item].x; cur_sel_x.text = root.vram_images[current-item].x;
cur_sel_y.display_value = root.vram_images[current-item].y; cur_sel_y.text = root.vram_images[current-item].y;
cur_sel_img.source = root.vram_images[current-item].img; cur_sel_img.source = root.vram_images[current-item].img;
cur_sel_img.visible = true; cur_sel_img.visible = true;
} }
} }
HorizontalLayout { HorizontalLayout {
@ -122,8 +156,8 @@ export component MainTab inherits Rectangle {
clicked => { clicked => {
root.remove_file_clicked(vram_files_list.current_item); root.remove_file_clicked(vram_files_list.current_item);
vram_files_list.current-item = -1; vram_files_list.current-item = -1;
cur_sel_x.display_value = 0; cur_sel_x.text = 0;
cur_sel_y.display_value = 0; cur_sel_y.text = 0;
cur_sel_img.visible = false; cur_sel_img.visible = false;
} }
} }
@ -144,13 +178,47 @@ export component MainTab inherits Rectangle {
image-fit: contain; image-fit: contain;
} }
} }
cur_sel_x := Text { HorizontalLayout {
in-out property <int> display_value; alignment: start;
text: "X: " + display_value; VerticalLayout {
alignment: center;
Text {
text: "X: ";
}
}
cur_sel_x := LineEdit {
input-type: number;
text: 0;
width: 64pt;
accepted(text) => {
if(vram_files_list.current-item != -1) {
root.move_vram_image(vram_files_list.current-item, text.to-float() - vram_images[vram_files_list.current-item].x, 0);
self.text = vram_images[vram_files_list.current-item].x;
}
}
}
} }
cur_sel_y :=Text { HorizontalLayout {
in-out property <int> display_value; alignment: start;
text: "Y: " + display_value; VerticalLayout {
alignment: center;
Text {
text: "Y: ";
}
}
cur_sel_y := LineEdit {
input-type: number;
text: 0;
width: 64pt;
accepted(text) => {
if(vram_files_list.current-item != -1) {
root.move_vram_image(vram_files_list.current-item, 0, text.to-float() - vram_images[vram_files_list.current-item].y);
self.text = vram_images[vram_files_list.current-item].y;
}
}
}
} }
ComboBox { ComboBox {
model: ["4-bit", "16-bit", "24-bit"]; model: ["4-bit", "16-bit", "24-bit"];
@ -162,6 +230,33 @@ export component MainTab inherits Rectangle {
} }
} }
FocusScope {
key-pressed(event) => {
if(vram_files_list.current-item != -1) {
if(event.text == Key.LeftArrow) {
root.move_vram_image(vram_files_list.current-item, -1, 0);
cur_sel_x.text = vram_images[vram_files_list.current-item].x;
}
if(event.text == Key.RightArrow) {
root.move_vram_image(vram_files_list.current-item, 1, 0);
cur_sel_x.text = vram_images[vram_files_list.current-item].x;
}
if(event.text == Key.UpArrow) {
root.move_vram_image(vram_files_list.current-item, 0, -1);
cur_sel_y.text = vram_images[vram_files_list.current-item].y;
}
if(event.text == Key.DownArrow) {
root.move_vram_image(vram_files_list.current-item, 0, 1);
cur_sel_y.text = vram_images[vram_files_list.current-item].y;
}
}
accept
}
}
function get_border_width() -> int { function get_border_width() -> int {
return 4; return 4;
} }

View File

@ -3,14 +3,17 @@ component VRAMSegment inherits Rectangle {
in property <image> img; in property <image> img;
in property <int> clip_x; in property <int> clip_x;
in property <int> clip_y; in property <int> clip_y;
in property <int> scale: 1;
width: 64px; width: 64px*scale;
height: 256px; height: 256px*scale;
clip: true; clip: true;
Image { Image {
source: img; source: img;
x: -root.clip_x*1px; x: -root.clip_x*1px*scale;
y: -root.clip_y*1px; y: -root.clip_y*1px*scale;
width: img.width*1px*scale;
height: img.height*1px*scale;
} }
} }
@ -18,17 +21,19 @@ export component VRAMArea inherits Rectangle {
in property <image> img; in property <image> img;
in property <int> img_x; in property <int> img_x;
in property <int> img_y; in property <int> img_y;
in property <int> scale: 1;
width: (64*16+15)*1px; width: ((64*16+15)*1px)*scale;
height: (256*2+1)*1px; height: ((256*2+1)*1px)*scale;
for idx in 32 : VRAMSegment { for idx in 32 : VRAMSegment {
x: root.get_x(idx)*(64px + 1px); x: (root.get_x(idx)*(64px + 1px))*scale;
y: root.get_y(idx)*(256px + 1px); y: (root.get_y(idx)*(256px + 1px))*scale;
img: img; img: img;
clip_x: (root.get_x(idx)*64) - root.img_x; clip_x: (root.get_x(idx)*64) - root.img_x;
clip_y: (root.get_y(idx)*256) - root.img_y; clip_y: (root.get_y(idx)*256) - root.img_y;
scale: root.scale;
} }
function get_x(idx: int) -> int { function get_x(idx: int) -> int {