mirror of
https://github.com/13hannes11/focus_annotator.git
synced 2024-09-03 23:21:01 +02:00
moving UI into seperate module
This commit is contained in:
85
Cargo.lock
generated
85
Cargo.lock
generated
@@ -74,6 +74,72 @@ version = "1.0.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling"
|
||||||
|
version = "0.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5f2c43f534ea4b0b049015d00269734195e6d3f0f6635cb692251aca6f9f8b3c"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"darling_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_core"
|
||||||
|
version = "0.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8e91455b86830a1c21799d94524df0845183fa55bafd9aa137b01c7d1065fa36"
|
||||||
|
dependencies = [
|
||||||
|
"fnv",
|
||||||
|
"ident_case",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"strsim",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "darling_macro"
|
||||||
|
version = "0.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "29b5acf0dea37a7f66f7b25d2c5e93fd46f8f6968b1a5d7a3e02e97768afc95a"
|
||||||
|
dependencies = [
|
||||||
|
"darling_core",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_builder"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d13202debe11181040ae9063d739fa32cfcaaebe2275fe387703460ae2365b30"
|
||||||
|
dependencies = [
|
||||||
|
"derive_builder_macro",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_builder_core"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "66e616858f6187ed828df7c64a6d71720d83767a7f19740b2d1b6fe6327b36e5"
|
||||||
|
dependencies = [
|
||||||
|
"darling",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "derive_builder_macro"
|
||||||
|
version = "0.10.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "58a94ace95092c5acb1e97a7e846b310cfbd499652f72297da7493f618a98d73"
|
||||||
|
dependencies = [
|
||||||
|
"derive_builder_core",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "field-offset"
|
name = "field-offset"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
@@ -84,10 +150,17 @@ dependencies = [
|
|||||||
"rustc_version",
|
"rustc_version",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "focus-annotator"
|
name = "focus-annotator"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"derive_builder",
|
||||||
"gls",
|
"gls",
|
||||||
"gtk4",
|
"gtk4",
|
||||||
"libadwaita",
|
"libadwaita",
|
||||||
@@ -441,6 +514,12 @@ version = "0.4.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ident_case"
|
||||||
|
version = "1.0.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "itoa"
|
name = "itoa"
|
||||||
version = "1.0.1"
|
version = "1.0.1"
|
||||||
@@ -806,6 +885,12 @@ version = "1.7.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
|
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.10.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "syn"
|
name = "syn"
|
||||||
version = "1.0.82"
|
version = "1.0.82"
|
||||||
|
|||||||
@@ -11,4 +11,4 @@ gtk = { version = "0.4", package = "gtk4" }
|
|||||||
gls = { version = "0.1" }
|
gls = { version = "0.1" }
|
||||||
serde = { version = "1.0", features = ["derive"]}
|
serde = { version = "1.0", features = ["derive"]}
|
||||||
serde_json = { version = "1.0" }
|
serde_json = { version = "1.0" }
|
||||||
|
derive_builder = "0.10"
|
||||||
|
|||||||
262
src/main.rs
262
src/main.rs
@@ -1,109 +1,27 @@
|
|||||||
|
#[macro_use]
|
||||||
|
extern crate derive_builder;
|
||||||
|
|
||||||
mod state;
|
mod state;
|
||||||
|
mod ui;
|
||||||
mod constants;
|
mod constants;
|
||||||
|
|
||||||
pub use crate::state::AnnotationImage;
|
pub use crate::state::AnnotationImage;
|
||||||
pub use crate::constants::MARGIN_BOTTOM;
|
pub use crate::constants::MARGIN_BOTTOM;
|
||||||
|
use crate::state::AnnotationZStack;
|
||||||
|
pub use crate::ui::ImageUI;
|
||||||
|
|
||||||
|
|
||||||
use std::cell::{RefCell};
|
use std::cell::{RefCell};
|
||||||
|
use std::fs;
|
||||||
use std::sync::{Arc};
|
use std::sync::{Arc};
|
||||||
use std::{fs};
|
|
||||||
|
|
||||||
use adw::{prelude::*, ApplicationWindow, HeaderBar, SplitButton};
|
use adw::{prelude::*, Application};
|
||||||
use constants::{MARGIN_TOP, MARGIN_LEFT, MARGIN_RIGHT_SCALE_ADDITIONAL, TOGGLE_NEIGHBOURS_TEXT, TOGGLE_NEIGHBOURS_TEXT_TOGGLED, SCALE_STEP};
|
use constants::{TOGGLE_NEIGHBOURS_TEXT_TOGGLED, TOGGLE_NEIGHBOURS_TEXT, SCALE_STEP};
|
||||||
use gio::SimpleAction;
|
|
||||||
use glib::clone;
|
use glib::clone;
|
||||||
use gtk::{gio, glib, FileChooserAction, FileChooserDialog, ResponseType};
|
use gtk::gio::SimpleAction;
|
||||||
use gtk::{
|
use gtk::{glib, FileChooserDialog, FileChooserAction, ResponseType, FileFilter};
|
||||||
ActionBar, Application, AspectFrame, Box, Button, FileFilter, Grid, Image, Orientation,
|
|
||||||
PositionType, Scale, Separator, ToggleButton,
|
|
||||||
};
|
|
||||||
use state::{AnnotationZStack, State};
|
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
use state::{State};
|
||||||
struct ImageUI {
|
|
||||||
individual: Arc<Image>,
|
|
||||||
center: Arc<Image>,
|
|
||||||
neighbours: [Arc<Image>; 8],
|
|
||||||
focus_scale: Arc<Scale>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ImageUI {
|
|
||||||
pub fn new() -> ImageUI {
|
|
||||||
let individual = Arc::new(Image::builder().vexpand(true).hexpand(true).build());
|
|
||||||
let center = Arc::new(Image::builder().vexpand(true).hexpand(true).build());
|
|
||||||
let neighbours = [
|
|
||||||
Arc::new(Image::builder().vexpand(true).hexpand(true).build()),
|
|
||||||
Arc::new(Image::builder().vexpand(true).hexpand(true).build()),
|
|
||||||
Arc::new(Image::builder().vexpand(true).hexpand(true).build()),
|
|
||||||
Arc::new(Image::builder().vexpand(true).hexpand(true).build()),
|
|
||||||
Arc::new(Image::builder().vexpand(true).hexpand(true).build()),
|
|
||||||
Arc::new(Image::builder().vexpand(true).hexpand(true).build()),
|
|
||||||
Arc::new(Image::builder().vexpand(true).hexpand(true).build()),
|
|
||||||
Arc::new(Image::builder().vexpand(true).hexpand(true).build()),
|
|
||||||
];
|
|
||||||
let focus_scale = Arc::new(
|
|
||||||
Scale::builder()
|
|
||||||
.orientation(Orientation::Vertical)
|
|
||||||
.vexpand(true)
|
|
||||||
.margin_top(MARGIN_TOP)
|
|
||||||
.margin_bottom(MARGIN_BOTTOM)
|
|
||||||
.margin_start(MARGIN_LEFT)
|
|
||||||
.draw_value(true)
|
|
||||||
.inverted(true)
|
|
||||||
.round_digits(0)
|
|
||||||
.digits(0)
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
ImageUI {
|
|
||||||
individual,
|
|
||||||
center,
|
|
||||||
neighbours,
|
|
||||||
focus_scale,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
pub fn update(&self, state : &State) {
|
|
||||||
if let Some(annotation_image) = state.get_current_annotation_image() {
|
|
||||||
self.update_image(&annotation_image);
|
|
||||||
}
|
|
||||||
self.update_focus_scale(&state);
|
|
||||||
}
|
|
||||||
fn update_image(&self, annotation_image: &AnnotationImage) {
|
|
||||||
self.individual
|
|
||||||
.set_from_file(Some(annotation_image.image_path.clone()));
|
|
||||||
self.center
|
|
||||||
.set_from_file(Some(annotation_image.image_path.clone()));
|
|
||||||
|
|
||||||
for index in 0..annotation_image.neighbours.len() {
|
|
||||||
self.neighbours[index].set_from_file(annotation_image.neighbours[index].clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn update_focus_scale(&self, state: &State) {
|
|
||||||
let max = state.get_current_foucs_stack_max().unwrap_or(0) as f64;
|
|
||||||
self.focus_scale.set_range(0.0, max);
|
|
||||||
|
|
||||||
if let Some(best_index) = state.get_current_foucs_stack_best_index() {
|
|
||||||
self.focus_scale.clear_marks();
|
|
||||||
self.focus_scale.add_mark(
|
|
||||||
best_index as f64,
|
|
||||||
PositionType::Right,
|
|
||||||
Some("focus"),
|
|
||||||
);
|
|
||||||
self.focus_scale.set_margin_end(0);
|
|
||||||
} else {
|
|
||||||
self.focus_scale.clear_marks();
|
|
||||||
self.focus_scale.set_margin_end(MARGIN_RIGHT_SCALE_ADDITIONAL);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(current_value) = state.image_index {
|
|
||||||
self.focus_scale.set_value(current_value as f64);
|
|
||||||
} else {
|
|
||||||
self.focus_scale.set_value(f64::floor(max / 2.0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let application = Application::builder()
|
let application = Application::builder()
|
||||||
@@ -114,21 +32,12 @@ fn main() {
|
|||||||
adw::init();
|
adw::init();
|
||||||
});
|
});
|
||||||
|
|
||||||
application.connect_startup(setup_shortcuts);
|
application.connect_startup(ImageUI::setup_shortcuts);
|
||||||
application.connect_activate(build_ui);
|
application.connect_activate(build_ui);
|
||||||
|
|
||||||
application.run();
|
application.run();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn setup_shortcuts(app: &Application) {
|
|
||||||
app.set_accels_for_action("win.toggle_neighbour", &["G"]);
|
|
||||||
app.set_accels_for_action("win.increment_focus_scale", &["W"]);
|
|
||||||
app.set_accels_for_action("win.decrement_focus_scale", &["S"]);
|
|
||||||
app.set_accels_for_action("win.mark_focus", &["M"]);
|
|
||||||
app.set_accels_for_action("win.skip_focus", &["N"]);
|
|
||||||
app.set_accels_for_action("win.back_focus", &["B"]);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
fn build_ui(app: &Application) {
|
fn build_ui(app: &Application) {
|
||||||
|
|
||||||
@@ -138,52 +47,8 @@ fn build_ui(app: &Application) {
|
|||||||
// MAIN CONTENT //
|
// MAIN CONTENT //
|
||||||
//////////////////
|
//////////////////
|
||||||
|
|
||||||
let image_ui = Arc::new(ImageUI::new());
|
let image_ui = Arc::new(ImageUI::new(app));
|
||||||
image_ui.update(&(state.as_ref().borrow()));
|
//image_ui.build_ui();
|
||||||
|
|
||||||
let focus_neighbours_grid = Arc::new(
|
|
||||||
Grid::builder()
|
|
||||||
.vexpand(true)
|
|
||||||
.hexpand(true)
|
|
||||||
.column_spacing(0)
|
|
||||||
.row_spacing(0)
|
|
||||||
.build(),
|
|
||||||
);
|
|
||||||
|
|
||||||
let focus_neighbours_aspect_frame = AspectFrame::builder()
|
|
||||||
.ratio(1.0)
|
|
||||||
.xalign(0.5)
|
|
||||||
.yalign(0.5)
|
|
||||||
.build();
|
|
||||||
focus_neighbours_aspect_frame.set_child(Some(image_ui.individual.as_ref()));
|
|
||||||
|
|
||||||
focus_neighbours_grid.attach(image_ui.center.as_ref(), 1, 1, 1, 1);
|
|
||||||
|
|
||||||
for index in 0..image_ui.neighbours.len() {
|
|
||||||
// offset index for later images to leave out middle of the grid
|
|
||||||
let grid_index: i32 = if index > 3 { index + 1 } else { index }
|
|
||||||
.try_into()
|
|
||||||
.unwrap();
|
|
||||||
let column = grid_index % 3;
|
|
||||||
let row = grid_index / 3;
|
|
||||||
focus_neighbours_grid.attach(image_ui.neighbours[index].as_ref(), column, row, 1, 1);
|
|
||||||
eprintln!("{column} {row}");
|
|
||||||
}
|
|
||||||
|
|
||||||
//let focus_scale = image_ui.focus_scale.clone();
|
|
||||||
|
|
||||||
// update_focus_scale(focus_scale.as_ref(), z_stack.clone());
|
|
||||||
|
|
||||||
let center_content_seperator = Separator::new(Orientation::Vertical);
|
|
||||||
let center_content = Box::builder()
|
|
||||||
//.hexpand(true)
|
|
||||||
.orientation(Orientation::Horizontal)
|
|
||||||
.spacing(0)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
center_content.append(image_ui.focus_scale.as_ref());
|
|
||||||
center_content.append(¢er_content_seperator);
|
|
||||||
center_content.append(&focus_neighbours_aspect_frame);
|
|
||||||
|
|
||||||
image_ui.focus_scale.connect_value_changed(clone!(@strong image_ui, @strong state => move |x| {
|
image_ui.focus_scale.connect_value_changed(clone!(@strong image_ui, @strong state => move |x| {
|
||||||
let index = x.value() as usize;
|
let index = x.value() as usize;
|
||||||
@@ -191,97 +56,39 @@ fn build_ui(app: &Application) {
|
|||||||
image_ui.update(&state.borrow());
|
image_ui.update(&state.borrow());
|
||||||
}));
|
}));
|
||||||
|
|
||||||
////////////
|
|
||||||
// HEADER //
|
|
||||||
////////////
|
|
||||||
|
|
||||||
//let show_start_title_buttons = Button::new();
|
|
||||||
let header_bar = HeaderBar::builder()
|
|
||||||
.title_widget(&adw::WindowTitle::new("First App", ""))
|
|
||||||
.build();
|
|
||||||
|
|
||||||
// TODO: add button functionality
|
|
||||||
let open_button = SplitButton::builder().label("Open").build();
|
|
||||||
header_bar.pack_start(&open_button);
|
|
||||||
|
|
||||||
////////////////////
|
////////////////////
|
||||||
// BOTTOM TOOLBAR //
|
// BOTTOM TOOLBAR //
|
||||||
///////////////////
|
///////////////////
|
||||||
|
image_ui.back_button.connect_clicked(|button| {
|
||||||
let bottom_toolbar = ActionBar::builder().build();
|
|
||||||
|
|
||||||
let back_button = Button::builder().label("Back").build();
|
|
||||||
|
|
||||||
back_button.connect_clicked(|button| {
|
|
||||||
button.activate_action("win.back_focus", None)
|
button.activate_action("win.back_focus", None)
|
||||||
.expect("The action does not exist.");
|
.expect("The action does not exist.");
|
||||||
});
|
});
|
||||||
|
|
||||||
let skip_button = Button::builder().label("Skip").build();
|
image_ui.skip_button.connect_clicked(|button| {
|
||||||
|
|
||||||
skip_button.connect_clicked(|button| {
|
|
||||||
button.activate_action("win.skip_focus", None)
|
button.activate_action("win.skip_focus", None)
|
||||||
.expect("The action does not exist.");
|
.expect("The action does not exist.");
|
||||||
});
|
});
|
||||||
|
|
||||||
let focus_button = Button::builder()
|
image_ui.focus_button.connect_clicked(|button| {
|
||||||
.label("Set Focus")
|
|
||||||
.css_classes(vec!["suggested-action".to_string()])
|
|
||||||
.build();
|
|
||||||
|
|
||||||
focus_button.connect_clicked(|button| {
|
|
||||||
button.activate_action("win.mark_focus", None)
|
button.activate_action("win.mark_focus", None)
|
||||||
.expect("The action does not exist.");
|
.expect("The action does not exist.");
|
||||||
});
|
});
|
||||||
let focus_skip_link_widget = Box::builder()
|
|
||||||
.css_classes(vec!["linked".to_string()])
|
|
||||||
.build();
|
|
||||||
focus_skip_link_widget.append(&back_button);
|
|
||||||
focus_skip_link_widget.append(&skip_button);
|
|
||||||
focus_skip_link_widget.append(&focus_button);
|
|
||||||
|
|
||||||
let neighbour_toggle_button = ToggleButton::builder()
|
|
||||||
.label(TOGGLE_NEIGHBOURS_TEXT)
|
|
||||||
.width_request(158)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
let focus_image = image_ui.individual.clone();
|
let focus_image = image_ui.individual.clone();
|
||||||
neighbour_toggle_button.connect_toggled(
|
image_ui.neighbour_toggle_button.connect_toggled(
|
||||||
clone!(@strong focus_neighbours_grid => move |x| match x.is_active() {
|
clone!(@strong image_ui => move |x| match x.is_active() {
|
||||||
true => {
|
true => {
|
||||||
focus_neighbours_aspect_frame.set_child(Some(focus_neighbours_grid.as_ref()));
|
image_ui.focus_neighbours_aspect_frame.set_child(Some(image_ui.focus_neighbours_grid.as_ref()));
|
||||||
x.set_label(TOGGLE_NEIGHBOURS_TEXT_TOGGLED);
|
x.set_label(TOGGLE_NEIGHBOURS_TEXT_TOGGLED);
|
||||||
}
|
}
|
||||||
false => {
|
false => {
|
||||||
focus_neighbours_aspect_frame.set_child(Some(focus_image.as_ref()));
|
image_ui.focus_neighbours_aspect_frame.set_child(Some(focus_image.as_ref()));
|
||||||
x.set_label(TOGGLE_NEIGHBOURS_TEXT);
|
x.set_label(TOGGLE_NEIGHBOURS_TEXT);
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
bottom_toolbar.pack_start(&neighbour_toggle_button);
|
|
||||||
bottom_toolbar.pack_end(&focus_skip_link_widget);
|
|
||||||
|
|
||||||
//////////////////////
|
image_ui.open_button.connect_clicked(clone!(@strong image_ui, @strong state => move |_| {
|
||||||
// MAIN APPLICATION //
|
|
||||||
//////////////////////
|
|
||||||
|
|
||||||
// Combine the content in a box
|
|
||||||
let application_vertical_widget = Box::new(Orientation::Vertical, 0);
|
|
||||||
// Adwaitas' ApplicationWindow does not include a HeaderBar
|
|
||||||
|
|
||||||
application_vertical_widget.append(&header_bar);
|
|
||||||
application_vertical_widget.append(¢er_content);
|
|
||||||
application_vertical_widget.append(&bottom_toolbar);
|
|
||||||
|
|
||||||
let window = ApplicationWindow::builder()
|
|
||||||
.application(app)
|
|
||||||
.default_width(800)
|
|
||||||
.default_height(600)
|
|
||||||
// add content to window
|
|
||||||
.content(&application_vertical_widget)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
open_button.connect_clicked(clone!(@weak window, @strong image_ui, @strong state => move |_| {
|
|
||||||
// TODO: actually open and load data
|
// TODO: actually open and load data
|
||||||
|
|
||||||
|
|
||||||
@@ -290,11 +97,11 @@ fn build_ui(app: &Application) {
|
|||||||
let filter = FileFilter::new();
|
let filter = FileFilter::new();
|
||||||
filter.add_pattern(r"*.json");
|
filter.add_pattern(r"*.json");
|
||||||
|
|
||||||
let file_chooser = Arc::new(FileChooserDialog::new(Some("Chose a data file!"), Some(&window), file_chooser_action, &buttons));
|
let file_chooser = Arc::new(FileChooserDialog::new(Some("Chose a data file!"), Some(image_ui.window.as_ref()), file_chooser_action, &buttons));
|
||||||
file_chooser.set_select_multiple(false);
|
file_chooser.set_select_multiple(false);
|
||||||
file_chooser.set_filter(&filter);
|
file_chooser.set_filter(&filter);
|
||||||
|
|
||||||
file_chooser.connect_response(clone!(@weak window, @strong image_ui, @weak state => move |dialog: &FileChooserDialog, response: ResponseType| {
|
file_chooser.connect_response(clone!(@strong image_ui, @weak state => move |dialog: &FileChooserDialog, response: ResponseType| {
|
||||||
if response == ResponseType::Ok {
|
if response == ResponseType::Ok {
|
||||||
let file = dialog.file().expect("Couldn't get file");
|
let file = dialog.file().expect("Couldn't get file");
|
||||||
eprintln!("Open");
|
eprintln!("Open");
|
||||||
@@ -320,8 +127,8 @@ fn build_ui(app: &Application) {
|
|||||||
////////////////////////
|
////////////////////////
|
||||||
|
|
||||||
let action_toggle_neighbour = SimpleAction::new("toggle_neighbour", None);
|
let action_toggle_neighbour = SimpleAction::new("toggle_neighbour", None);
|
||||||
action_toggle_neighbour.connect_activate(clone!(@weak window => move |_, _| {
|
action_toggle_neighbour.connect_activate(clone!(@strong image_ui => move |_, _| {
|
||||||
neighbour_toggle_button.set_active(!neighbour_toggle_button.is_active());
|
image_ui.neighbour_toggle_button.set_active(!image_ui.neighbour_toggle_button.is_active());
|
||||||
}));
|
}));
|
||||||
|
|
||||||
let action_focus_scale_increment = SimpleAction::new("increment_focus_scale", None);
|
let action_focus_scale_increment = SimpleAction::new("increment_focus_scale", None);
|
||||||
@@ -358,12 +165,11 @@ fn build_ui(app: &Application) {
|
|||||||
image_ui.update(&state);
|
image_ui.update(&state);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
window.add_action(&action_toggle_neighbour);
|
image_ui.window.add_action(&action_toggle_neighbour);
|
||||||
window.add_action(&action_focus_scale_increment);
|
image_ui.window.add_action(&action_focus_scale_increment);
|
||||||
window.add_action(&action_focus_scale_decrement);
|
image_ui.window.add_action(&action_focus_scale_decrement);
|
||||||
window.add_action(&mark_focus);
|
image_ui.window.add_action(&mark_focus);
|
||||||
window.add_action(&skip_focus);
|
image_ui.window.add_action(&skip_focus);
|
||||||
window.add_action(&back_focus);
|
image_ui.window.add_action(&back_focus);
|
||||||
|
image_ui.show();
|
||||||
window.show();
|
|
||||||
}
|
}
|
||||||
245
src/ui/mod.rs
Normal file
245
src/ui/mod.rs
Normal file
@@ -0,0 +1,245 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
use adw::{Application, ApplicationWindow, HeaderBar, SplitButton};
|
||||||
|
use gtk::{
|
||||||
|
traits::{BoxExt, GridExt, GtkApplicationExt, RangeExt, ScaleExt, WidgetExt},
|
||||||
|
ActionBar, AspectFrame, Box, Button, Grid, Image, Orientation, PositionType, Scale, Separator,
|
||||||
|
ToggleButton,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
constants::{MARGIN_LEFT, MARGIN_RIGHT_SCALE_ADDITIONAL, MARGIN_TOP, TOGGLE_NEIGHBOURS_TEXT},
|
||||||
|
state::State,
|
||||||
|
AnnotationImage, MARGIN_BOTTOM,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Builder)]
|
||||||
|
pub struct ImageUI {
|
||||||
|
pub window: Arc<ApplicationWindow>,
|
||||||
|
pub application_vertical_widget: Arc<Box>,
|
||||||
|
pub individual: Arc<Image>,
|
||||||
|
pub center: Arc<Image>,
|
||||||
|
pub neighbours: [Arc<Image>; 8],
|
||||||
|
pub focus_scale: Arc<Scale>,
|
||||||
|
pub focus_neighbours_grid: Arc<Grid>,
|
||||||
|
pub focus_neighbours_aspect_frame: Arc<AspectFrame>,
|
||||||
|
|
||||||
|
pub neighbour_toggle_button: Arc<ToggleButton>,
|
||||||
|
pub open_button: Arc<SplitButton>,
|
||||||
|
pub back_button: Arc<Button>,
|
||||||
|
pub skip_button: Arc<Button>,
|
||||||
|
pub focus_button: Arc<Button>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ImageUI {
|
||||||
|
pub fn new(app: &Application) -> ImageUI {
|
||||||
|
let mut builder = ImageUIBuilder::default();
|
||||||
|
let application_vertical_widget = Arc::new(Box::new(Orientation::Vertical, 0));
|
||||||
|
|
||||||
|
let window = Arc::new(
|
||||||
|
ApplicationWindow::builder()
|
||||||
|
.application(app)
|
||||||
|
.default_width(800)
|
||||||
|
.default_height(600)
|
||||||
|
// add content to window
|
||||||
|
.content(application_vertical_widget.as_ref())
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.application_vertical_widget(application_vertical_widget.clone())
|
||||||
|
.window(window);
|
||||||
|
|
||||||
|
// TODO: move into builder
|
||||||
|
ImageUI::build_header(&mut builder, application_vertical_widget.clone());
|
||||||
|
ImageUI::build_center(&mut builder, application_vertical_widget.clone());
|
||||||
|
ImageUI::build_bottom_toolbar(&mut builder, application_vertical_widget.clone());
|
||||||
|
|
||||||
|
builder.build().unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_header(builder: &mut ImageUIBuilder, application_vertical_widget: Arc<Box>) {
|
||||||
|
let header_bar = HeaderBar::builder()
|
||||||
|
.title_widget(&adw::WindowTitle::new("First App", ""))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
// TODO: add button functionality
|
||||||
|
let open_button = Arc::new(SplitButton::builder().label("Open").build());
|
||||||
|
header_bar.pack_start(open_button.as_ref());
|
||||||
|
application_vertical_widget.append(&header_bar);
|
||||||
|
|
||||||
|
builder.open_button(open_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_center(builder: &mut ImageUIBuilder, application_vertical_widget: Arc<Box>) {
|
||||||
|
let individual = Arc::new(Image::builder().vexpand(true).hexpand(true).build());
|
||||||
|
let center = Arc::new(Image::builder().vexpand(true).hexpand(true).build());
|
||||||
|
let neighbours = [
|
||||||
|
Arc::new(Image::builder().vexpand(true).hexpand(true).build()),
|
||||||
|
Arc::new(Image::builder().vexpand(true).hexpand(true).build()),
|
||||||
|
Arc::new(Image::builder().vexpand(true).hexpand(true).build()),
|
||||||
|
Arc::new(Image::builder().vexpand(true).hexpand(true).build()),
|
||||||
|
Arc::new(Image::builder().vexpand(true).hexpand(true).build()),
|
||||||
|
Arc::new(Image::builder().vexpand(true).hexpand(true).build()),
|
||||||
|
Arc::new(Image::builder().vexpand(true).hexpand(true).build()),
|
||||||
|
Arc::new(Image::builder().vexpand(true).hexpand(true).build()),
|
||||||
|
];
|
||||||
|
|
||||||
|
let focus_scale = Arc::new(
|
||||||
|
Scale::builder()
|
||||||
|
.orientation(Orientation::Vertical)
|
||||||
|
.vexpand(true)
|
||||||
|
.margin_top(MARGIN_TOP)
|
||||||
|
.margin_bottom(MARGIN_BOTTOM)
|
||||||
|
.margin_start(MARGIN_LEFT)
|
||||||
|
.draw_value(true)
|
||||||
|
.inverted(true)
|
||||||
|
.round_digits(0)
|
||||||
|
.digits(0)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let focus_neighbours_grid = Arc::new(
|
||||||
|
Grid::builder()
|
||||||
|
.vexpand(true)
|
||||||
|
.hexpand(true)
|
||||||
|
.column_spacing(0)
|
||||||
|
.row_spacing(0)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
focus_neighbours_grid.attach(center.as_ref(), 1, 1, 1, 1);
|
||||||
|
|
||||||
|
for index in 0..neighbours.len() {
|
||||||
|
// offset index for later images to leave out middle of the grid
|
||||||
|
let grid_index: i32 = if index > 3 { index + 1 } else { index }
|
||||||
|
.try_into()
|
||||||
|
.unwrap();
|
||||||
|
let column = grid_index % 3;
|
||||||
|
let row = grid_index / 3;
|
||||||
|
focus_neighbours_grid.attach(neighbours[index].as_ref(), column, row, 1, 1);
|
||||||
|
eprintln!("{column} {row}");
|
||||||
|
}
|
||||||
|
|
||||||
|
let center_content_seperator = Separator::new(Orientation::Vertical);
|
||||||
|
let center_content = Box::builder()
|
||||||
|
.orientation(Orientation::Horizontal)
|
||||||
|
.spacing(0)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
let focus_neighbours_aspect_frame = Arc::new(
|
||||||
|
AspectFrame::builder()
|
||||||
|
.ratio(1.0)
|
||||||
|
.xalign(0.5)
|
||||||
|
.yalign(0.5)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
focus_neighbours_aspect_frame.set_child(Some(individual.as_ref()));
|
||||||
|
|
||||||
|
center_content.append(focus_scale.as_ref());
|
||||||
|
center_content.append(¢er_content_seperator);
|
||||||
|
center_content.append(focus_neighbours_aspect_frame.as_ref());
|
||||||
|
|
||||||
|
application_vertical_widget.append(¢er_content);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.focus_scale(focus_scale)
|
||||||
|
.focus_neighbours_grid(focus_neighbours_grid)
|
||||||
|
.focus_neighbours_aspect_frame(focus_neighbours_aspect_frame)
|
||||||
|
.individual(individual)
|
||||||
|
.center(center)
|
||||||
|
.neighbours(neighbours);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_bottom_toolbar(builder: &mut ImageUIBuilder, application_vertical_widget: Arc<Box>) {
|
||||||
|
let bottom_toolbar = ActionBar::builder().build();
|
||||||
|
|
||||||
|
let back_button = Arc::new(Button::builder().label("Back").build());
|
||||||
|
|
||||||
|
let skip_button = Arc::new(Button::builder().label("Skip").build());
|
||||||
|
|
||||||
|
let focus_button = Arc::new(
|
||||||
|
Button::builder()
|
||||||
|
.label("Set Focus")
|
||||||
|
.css_classes(vec!["suggested-action".to_string()])
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let neighbour_toggle_button = Arc::new(
|
||||||
|
ToggleButton::builder()
|
||||||
|
.label(TOGGLE_NEIGHBOURS_TEXT)
|
||||||
|
.width_request(158)
|
||||||
|
.build(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let focus_skip_link_widget = Box::builder()
|
||||||
|
.css_classes(vec!["linked".to_string()])
|
||||||
|
.build();
|
||||||
|
focus_skip_link_widget.append(back_button.as_ref());
|
||||||
|
focus_skip_link_widget.append(skip_button.as_ref());
|
||||||
|
focus_skip_link_widget.append(focus_button.as_ref());
|
||||||
|
|
||||||
|
bottom_toolbar.pack_start(neighbour_toggle_button.as_ref());
|
||||||
|
bottom_toolbar.pack_end(&focus_skip_link_widget);
|
||||||
|
|
||||||
|
application_vertical_widget.append(&bottom_toolbar);
|
||||||
|
|
||||||
|
builder
|
||||||
|
.neighbour_toggle_button(neighbour_toggle_button)
|
||||||
|
.back_button(back_button)
|
||||||
|
.skip_button(skip_button)
|
||||||
|
.focus_button(focus_button);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn show(&self) {
|
||||||
|
self.window.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn update(&self, state: &State) {
|
||||||
|
if let Some(annotation_image) = state.get_current_annotation_image() {
|
||||||
|
self.update_image(&annotation_image);
|
||||||
|
}
|
||||||
|
self.update_focus_scale(&state);
|
||||||
|
}
|
||||||
|
fn update_image(&self, annotation_image: &AnnotationImage) {
|
||||||
|
self.individual
|
||||||
|
.set_from_file(Some(annotation_image.image_path.clone()));
|
||||||
|
self.center
|
||||||
|
.set_from_file(Some(annotation_image.image_path.clone()));
|
||||||
|
|
||||||
|
for index in 0..annotation_image.neighbours.len() {
|
||||||
|
self.neighbours[index].set_from_file(annotation_image.neighbours[index].clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_focus_scale(&self, state: &State) {
|
||||||
|
let max = state.get_current_foucs_stack_max().unwrap_or(0) as f64;
|
||||||
|
self.focus_scale.set_range(0.0, max);
|
||||||
|
|
||||||
|
if let Some(best_index) = state.get_current_foucs_stack_best_index() {
|
||||||
|
self.focus_scale.clear_marks();
|
||||||
|
self.focus_scale
|
||||||
|
.add_mark(best_index as f64, PositionType::Right, Some("focus"));
|
||||||
|
self.focus_scale.set_margin_end(0);
|
||||||
|
} else {
|
||||||
|
self.focus_scale.clear_marks();
|
||||||
|
self.focus_scale
|
||||||
|
.set_margin_end(MARGIN_RIGHT_SCALE_ADDITIONAL);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(current_value) = state.image_index {
|
||||||
|
self.focus_scale.set_value(current_value as f64);
|
||||||
|
} else {
|
||||||
|
self.focus_scale.set_value(f64::floor(max / 2.0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn setup_shortcuts(app: &Application) {
|
||||||
|
app.set_accels_for_action("win.toggle_neighbour", &["G"]);
|
||||||
|
app.set_accels_for_action("win.increment_focus_scale", &["W"]);
|
||||||
|
app.set_accels_for_action("win.decrement_focus_scale", &["S"]);
|
||||||
|
app.set_accels_for_action("win.mark_focus", &["M"]);
|
||||||
|
app.set_accels_for_action("win.skip_focus", &["N"]);
|
||||||
|
app.set_accels_for_action("win.back_focus", &["B"]);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user