Finishing image storage in .exd and adding save and open capability.
This commit is contained in:
parent
3285ac3987
commit
de0bc9a655
@ -10,8 +10,12 @@ use Moo;
|
|||||||
use Path::Tiny;
|
use Path::Tiny;
|
||||||
use Archive::Zip;
|
use Archive::Zip;
|
||||||
|
|
||||||
|
use Exd::Utils;
|
||||||
|
|
||||||
use Digest::SHA qw/sha256_hex/;
|
use Digest::SHA qw/sha256_hex/;
|
||||||
|
|
||||||
|
use Exd::FileFormat::DB;
|
||||||
|
|
||||||
has dir => ( is => 'rw' );
|
has dir => ( is => 'rw' );
|
||||||
|
|
||||||
sub new_tmp($class) {
|
sub new_tmp($class) {
|
||||||
@ -22,21 +26,85 @@ sub new_tmp($class) {
|
|||||||
has _images_dir => ( is => 'lazy' );
|
has _images_dir => ( is => 'lazy' );
|
||||||
|
|
||||||
sub _build__images_dir($self) {
|
sub _build__images_dir($self) {
|
||||||
my $dir = $self->dir;
|
my $dir = $self->dir;
|
||||||
my $images = $dir->child('images');
|
my $images = $dir->child('images');
|
||||||
$images->mkpath;
|
$images->mkpath;
|
||||||
return $images;
|
return $images;
|
||||||
}
|
}
|
||||||
|
|
||||||
sub add_png_image( $self, $file_contents ) {
|
sub add_png_image( $self, $file_contents, $label = '' ) {
|
||||||
my $sha_image = sha256_hex($file_contents);
|
my $sha_image = sha256_hex($file_contents);
|
||||||
$self->_images_dir->child("$sha_image.png")->spew_raw($file_contents);
|
my $file = $self->_images_dir->child("$sha_image.png");
|
||||||
|
$file->spew_raw($file_contents);
|
||||||
|
if ( defined $label && '' . $label ) {
|
||||||
|
$self->_register_label( $label, $sha_image );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _image_hashes($self) {
|
||||||
|
my $glob = ( '' . $self->_images_dir ) . '/*.png';
|
||||||
|
my @images = glob( $glob );
|
||||||
|
@images = map {
|
||||||
|
my $image = path($_);
|
||||||
|
$image = $image->basename;
|
||||||
|
$image =~ s/\.png$//r;
|
||||||
|
} @images;
|
||||||
|
return \@images;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub image_hashes_to_label($self) {
|
||||||
|
my %images;
|
||||||
|
for my $image_hash ( $self->_image_hashes->@* ) {
|
||||||
|
my $dbh = $self->_dbh;
|
||||||
|
my $hashes = $dbh->selectall_arrayref(
|
||||||
|
'SELECT label FROM label_to_image_hash WHERE hash = ?',
|
||||||
|
{Slice => {}}, $image_hash );
|
||||||
|
$images{$image_hash} = [ sort { $a cmp $b } map { $_->{label} } @$hashes ];
|
||||||
|
}
|
||||||
|
return \%images;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _dbh($self) {
|
||||||
|
return Exd::FileFormat::DB->connect( '' . $self->dir->child('db.sqlite3') );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _register_label( $self, $label, $sha_image ) {
|
||||||
|
my $dbh = $self->_dbh;
|
||||||
|
eval {
|
||||||
|
$dbh->do(
|
||||||
|
'INSERT INTO label_to_image_hash (label, hash) VALUES (?, ?);',
|
||||||
|
{}, $label, $sha_image );
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
warn $@;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_image_gd_from_label ( $self, $label ) {
|
||||||
|
return $self->get_image_gd( $self->_get_hash_from_label($label) );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _get_hash_from_label($self, $label) {
|
||||||
|
my $dbh = $self->_dbh;
|
||||||
|
my $row = $dbh->selectrow_hashref(
|
||||||
|
'SELECT hash FROM label_to_image_hash WHERE label = ?',
|
||||||
|
{}, $label );
|
||||||
|
die 'No such image in file' if !defined $row || !defined $row->{hash};
|
||||||
|
return $row->{hash};
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_image_from_label ( $self, $label ) {
|
||||||
|
return $self->get_image( $self->_get_hash_from_label($label) );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub get_image( $self, $sha ) {
|
sub get_image( $self, $sha ) {
|
||||||
return $self->_images_dir->child("$sha.png");
|
return $self->_images_dir->child("$sha.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub get_image_gd ( $self, $sha ) {
|
||||||
|
return Exd::Utils::get_gd_image( $self->get_image($sha) );
|
||||||
|
}
|
||||||
|
|
||||||
sub get_script($self) {
|
sub get_script($self) {
|
||||||
return $self->dir->child('script.pl')->slurp_raw;
|
return $self->dir->child('script.pl')->slurp_raw;
|
||||||
}
|
}
|
||||||
@ -51,7 +119,7 @@ sub to_zip($self) {
|
|||||||
sub( $path, $state ) {
|
sub( $path, $state ) {
|
||||||
return if $path->is_dir;
|
return if $path->is_dir;
|
||||||
my $path_relative = $path->relative( $self->dir );
|
my $path_relative = $path->relative( $self->dir );
|
||||||
$zip->addFile( $path, $path_relative );
|
$zip->addFile( ''.$path, ''.$path_relative );
|
||||||
},
|
},
|
||||||
{ recurse => 1 }
|
{ recurse => 1 }
|
||||||
);
|
);
|
||||||
@ -61,7 +129,7 @@ sub to_zip($self) {
|
|||||||
sub from_zip_file( $class, $zip_file ) {
|
sub from_zip_file( $class, $zip_file ) {
|
||||||
my $tempdir = Path::Tiny->tempdir();
|
my $tempdir = Path::Tiny->tempdir();
|
||||||
my $zip = Archive::Zip->new($zip_file);
|
my $zip = Archive::Zip->new($zip_file);
|
||||||
$zip->extractTree( '.', $tempdir );
|
$zip->extractTree( '', $tempdir );
|
||||||
return $class->new( dir => $tempdir );
|
return $class->new( dir => $tempdir );
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +142,7 @@ sub execute( $self, $printer ) {
|
|||||||
if ($@) {
|
if ($@) {
|
||||||
die $@;
|
die $@;
|
||||||
}
|
}
|
||||||
$sub->($self, $printer);
|
$sub->( $self, $printer );
|
||||||
}
|
}
|
||||||
|
|
||||||
sub save( $self, $output_file ) {
|
sub save( $self, $output_file ) {
|
||||||
|
95
lib/Exd/FileFormat/DB.pm
Normal file
95
lib/Exd/FileFormat/DB.pm
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package Exd::FileFormat::DB;
|
||||||
|
|
||||||
|
use v5.40.0;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use utf8;
|
||||||
|
|
||||||
|
use DBI;
|
||||||
|
use Path::Tiny;
|
||||||
|
|
||||||
|
use Exd::FileFormat::DB::Migrations;
|
||||||
|
|
||||||
|
my $dbh;
|
||||||
|
|
||||||
|
sub connect {
|
||||||
|
if ( defined $dbh ) {
|
||||||
|
return $dbh;
|
||||||
|
}
|
||||||
|
my $class = shift or die 'Missing arg class';
|
||||||
|
my $database = shift or die 'Missing arg database file';
|
||||||
|
$dbh = DBI->connect(
|
||||||
|
"dbi:SQLite:dbname=$database",
|
||||||
|
,
|
||||||
|
undef, undef,
|
||||||
|
{
|
||||||
|
RaiseError => 1,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
$class->_migrate($dbh);
|
||||||
|
return $dbh;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _migrate {
|
||||||
|
my $class = shift;
|
||||||
|
my $dbh = shift;
|
||||||
|
local $dbh->{RaiseError} = 0;
|
||||||
|
local $dbh->{PrintError} = 0;
|
||||||
|
my @migrations = Exd::FileFormat::DB::Migrations::MIGRATIONS();
|
||||||
|
if ( $class->get_current_migration($dbh) > @migrations ) {
|
||||||
|
warn "Something happened there, wrong migration number.";
|
||||||
|
}
|
||||||
|
if ( $class->get_current_migration($dbh) >= @migrations ) {
|
||||||
|
say STDERR "Migrations already applied.";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
$class->_apply_migrations( $dbh, \@migrations );
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _apply_migrations {
|
||||||
|
my $class = shift;
|
||||||
|
my $dbh = shift;
|
||||||
|
my $migrations = shift;
|
||||||
|
for (
|
||||||
|
my $i = $class->get_current_migration($dbh) ;
|
||||||
|
$i < @$migrations ;
|
||||||
|
$i++
|
||||||
|
)
|
||||||
|
{
|
||||||
|
local $dbh->{RaiseError} = 1;
|
||||||
|
my $current_migration = $migrations->[$i];
|
||||||
|
my $migration_number = $i + 1;
|
||||||
|
$class->_apply_migration( $dbh, $current_migration, $migration_number );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _apply_migration {
|
||||||
|
my $class = shift;
|
||||||
|
my $dbh = shift;
|
||||||
|
my $current_migration = shift;
|
||||||
|
my $migration_number = shift;
|
||||||
|
{
|
||||||
|
if (ref $current_migration eq 'CODE') {
|
||||||
|
$current_migration->($dbh);
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
$dbh->do($current_migration);
|
||||||
|
}
|
||||||
|
$dbh->do( <<'EOF', undef, 'current_migration', $migration_number );
|
||||||
|
INSERT INTO options
|
||||||
|
VALUES ($1, $2)
|
||||||
|
ON CONFLICT (name) DO
|
||||||
|
UPDATE SET value = $2;
|
||||||
|
EOF
|
||||||
|
}
|
||||||
|
|
||||||
|
sub get_current_migration {
|
||||||
|
my $class = shift;
|
||||||
|
my $dbh = shift;
|
||||||
|
my $result = $dbh->selectrow_hashref( <<'EOF', undef, 'current_migration' );
|
||||||
|
select value from options where name = ?;
|
||||||
|
EOF
|
||||||
|
return int( $result->{value} // 0 );
|
||||||
|
}
|
||||||
|
1;
|
23
lib/Exd/FileFormat/DB/Migrations.pm
Normal file
23
lib/Exd/FileFormat/DB/Migrations.pm
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
package Exd::FileFormat::DB::Migrations;
|
||||||
|
|
||||||
|
use v5.40.0;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
use utf8;
|
||||||
|
|
||||||
|
use feature 'signatures';
|
||||||
|
|
||||||
|
sub MIGRATIONS {
|
||||||
|
return (
|
||||||
|
'CREATE TABLE options (
|
||||||
|
name TEXT PRIMARY KEY,
|
||||||
|
value TEXT
|
||||||
|
);',
|
||||||
|
'CREATE TABLE label_to_image_hash (
|
||||||
|
label TEXT NOT NULL PRIMARY KEY,
|
||||||
|
hash TEXT NOT NULL
|
||||||
|
);',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
1;
|
249
lib/Exd/Gui.pm
249
lib/Exd/Gui.pm
@ -38,6 +38,12 @@ Glib::Object::Introspection->setup(
|
|||||||
package => 'Gdk',
|
package => 'Gdk',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Glib::Object::Introspection->setup(
|
||||||
|
basename => 'GdkWayland',
|
||||||
|
version => '4.0',
|
||||||
|
package => 'GdkWayland',
|
||||||
|
);
|
||||||
|
|
||||||
Glib::Object::Introspection->setup(
|
Glib::Object::Introspection->setup(
|
||||||
basename => 'GtkSource',
|
basename => 'GtkSource',
|
||||||
version => '5',
|
version => '5',
|
||||||
@ -66,7 +72,6 @@ has _file_format => (
|
|||||||
is => 'rw',
|
is => 'rw',
|
||||||
default => sub {
|
default => sub {
|
||||||
my $tmp = Exd::FileFormat->new_tmp;
|
my $tmp = Exd::FileFormat->new_tmp;
|
||||||
say $tmp->dir;
|
|
||||||
$tmp->set_script(<<'EOF');
|
$tmp->set_script(<<'EOF');
|
||||||
sub ($exd, $printer) {
|
sub ($exd, $printer) {
|
||||||
$printer->print_text(
|
$printer->print_text(
|
||||||
@ -106,6 +111,11 @@ sub start($self) {
|
|||||||
$self->_daemon_script_runner;
|
$self->_daemon_script_runner;
|
||||||
my $app = Gtk4::Application->new( 'me.sergiotarxz.Exd', 'default-flags' );
|
my $app = Gtk4::Application->new( 'me.sergiotarxz.Exd', 'default-flags' );
|
||||||
$self->_app($app);
|
$self->_app($app);
|
||||||
|
$app->signal_connect(
|
||||||
|
startup => sub {
|
||||||
|
$self->_startup;
|
||||||
|
}
|
||||||
|
);
|
||||||
$app->signal_connect(
|
$app->signal_connect(
|
||||||
activate => sub {
|
activate => sub {
|
||||||
$self->_activate;
|
$self->_activate;
|
||||||
@ -114,6 +124,54 @@ sub start($self) {
|
|||||||
$app->run;
|
$app->run;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub _save_action($self) {
|
||||||
|
my $file_format = $self->_file_format;
|
||||||
|
my $dialog = Gtk4::FileDialog->new;
|
||||||
|
$dialog->set_initial_name('project.exd');
|
||||||
|
$dialog->save($self->window, undef, sub ($source, $res, $data) {
|
||||||
|
eval {
|
||||||
|
my $file = $dialog->save_finish($res);
|
||||||
|
$file = $file->get_path;
|
||||||
|
my $zip = $file_format->to_zip;
|
||||||
|
$zip->writeToFileNamed(''.$file);
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _open_action($self) {
|
||||||
|
my $file_format = $self->_file_format;
|
||||||
|
my $dialog = Gtk4::FileDialog->new;
|
||||||
|
$dialog->set_initial_name('project.exd');
|
||||||
|
$dialog->open($self->window, undef, sub ($source, $res, $data) {
|
||||||
|
eval {
|
||||||
|
my $file = $dialog->open_finish($res);
|
||||||
|
$file = $file->get_path;
|
||||||
|
$self->_file_format(Exd::FileFormat->from_zip_file($file));
|
||||||
|
$self->_update_editor_buffer;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _startup($self) {
|
||||||
|
my $app = $self->_app;
|
||||||
|
my $menu_model = Glib::IO::Menu->new;
|
||||||
|
my $file_menu = Glib::IO::Menu->new;
|
||||||
|
my $open_action = Glib::IO::SimpleAction->new('open', undef);
|
||||||
|
my $save_action = Glib::IO::SimpleAction->new('save', undef);
|
||||||
|
$open_action->signal_connect('activate', sub {
|
||||||
|
$self->_open_action;
|
||||||
|
});
|
||||||
|
$save_action->signal_connect('activate', sub {
|
||||||
|
$self->_save_action;
|
||||||
|
});
|
||||||
|
$app->add_action($open_action);
|
||||||
|
$app->add_action($save_action);
|
||||||
|
$file_menu->append('Open', 'app.open');
|
||||||
|
$file_menu->append('Save', 'app.save');
|
||||||
|
$menu_model->append_submenu('File', $file_menu);
|
||||||
|
$app->set_menubar($menu_model);
|
||||||
|
}
|
||||||
|
|
||||||
sub _generate_preview_file( $self, $verbose = 1 ) {
|
sub _generate_preview_file( $self, $verbose = 1 ) {
|
||||||
$self->_preview_file(1) if -f $self->_preview_file;
|
$self->_preview_file(1) if -f $self->_preview_file;
|
||||||
my $device =
|
my $device =
|
||||||
@ -137,7 +195,7 @@ sub _run_script( $self, $device = undef, $verbose = 1 ) {
|
|||||||
my $fh = $self->_write_to_script;
|
my $fh = $self->_write_to_script;
|
||||||
print $fh JSON::to_json(
|
print $fh JSON::to_json(
|
||||||
{
|
{
|
||||||
exd_dir => ''.$self->_file_format->dir,
|
exd_dir => '' . $self->_file_format->dir,
|
||||||
device => $device->serialize,
|
device => $device->serialize,
|
||||||
verbose => $verbose,
|
verbose => $verbose,
|
||||||
}
|
}
|
||||||
@ -179,8 +237,8 @@ sub _daemon_script_runner($self) {
|
|||||||
my $new_pid = fork;
|
my $new_pid = fork;
|
||||||
if ( !$new_pid ) {
|
if ( !$new_pid ) {
|
||||||
eval {
|
eval {
|
||||||
$self->_on_run_script( path($exd_dir), $device, $write_to_parent,
|
$self->_on_run_script( path($exd_dir), $device,
|
||||||
$verbose );
|
$write_to_parent, $verbose );
|
||||||
};
|
};
|
||||||
if ($@) {
|
if ($@) {
|
||||||
warn $@;
|
warn $@;
|
||||||
@ -219,12 +277,10 @@ sub _on_run_script( $self, $exd_dir, $device, $write_to_parent, $verbose = 1 ) {
|
|||||||
my $printer = Exd::Printer->new( device => $device );
|
my $printer = Exd::Printer->new( device => $device );
|
||||||
local $| = 1;
|
local $| = 1;
|
||||||
|
|
||||||
my $exd = Exd::FileFormat->new(dir => $exd_dir);
|
my $exd = Exd::FileFormat->new( dir => $exd_dir );
|
||||||
|
|
||||||
my ( $stdout, $stderr, $exit ) = capture {
|
my ( $stdout, $stderr, $exit ) = capture {
|
||||||
eval {
|
eval { $exd->execute($printer); };
|
||||||
$exd->execute($printer);
|
|
||||||
};
|
|
||||||
if ($@) {
|
if ($@) {
|
||||||
if ($verbose) {
|
if ($verbose) {
|
||||||
print $write_to_parent $@ . "\n";
|
print $write_to_parent $@ . "\n";
|
||||||
@ -279,6 +335,12 @@ sub _on_preview($self) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub _update_editor_buffer($self) {
|
||||||
|
my $editor = $self->_editor;
|
||||||
|
my $buffer = $editor->get_buffer();
|
||||||
|
$buffer->set_text( $self->_file_format->get_script, -1 );
|
||||||
|
}
|
||||||
|
|
||||||
sub _populate_editor( $self, $box_editor_preview ) {
|
sub _populate_editor( $self, $box_editor_preview ) {
|
||||||
my $editor = Gtk4::Source::View->new;
|
my $editor = Gtk4::Source::View->new;
|
||||||
$self->_editor($editor);
|
$self->_editor($editor);
|
||||||
@ -291,7 +353,7 @@ sub _populate_editor( $self, $box_editor_preview ) {
|
|||||||
$editor->set_smart_backspace(1);
|
$editor->set_smart_backspace(1);
|
||||||
$editor->set_show_line_numbers(1);
|
$editor->set_show_line_numbers(1);
|
||||||
my $buffer = $editor->get_buffer();
|
my $buffer = $editor->get_buffer();
|
||||||
$buffer->set_text( $self->_file_format->get_script, -1 );
|
$self->_update_editor_buffer;
|
||||||
$buffer->set_language(
|
$buffer->set_language(
|
||||||
Gtk4::Source::LanguageManager::get_default()->get_language('perl') );
|
Gtk4::Source::LanguageManager::get_default()->get_language('perl') );
|
||||||
$buffer->set_highlight_syntax(1);
|
$buffer->set_highlight_syntax(1);
|
||||||
@ -337,6 +399,166 @@ sub _populate_preview( $self, $box_editor_preview ) {
|
|||||||
$self->_generate_preview_file;
|
$self->_generate_preview_file;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub _open_gallery($self) {
|
||||||
|
my $window = Gtk4::Window->new;
|
||||||
|
$window->set_transient_for( $self->window );
|
||||||
|
$window->set_default_size( 600, 600 );
|
||||||
|
$window->set_title('Image gallery');
|
||||||
|
my $box = Gtk4::Box->new( 'vertical', 1 );
|
||||||
|
my $box_upper_menu = Gtk4::Box->new( 'horizontal', 10 );
|
||||||
|
my $add_image = Gtk4::Button->new_with_label('Add PNG image');
|
||||||
|
$add_image->set_halign('start');
|
||||||
|
my $label_entry_label =
|
||||||
|
Gtk4::Label->new('(Recommended) Put a label to the new image:');
|
||||||
|
my $entry_label = Gtk4::Entry->new;
|
||||||
|
$box_upper_menu->append($label_entry_label);
|
||||||
|
$box_upper_menu->append($entry_label);
|
||||||
|
my $images_scroll = Gtk4::ScrolledWindow->new;
|
||||||
|
$add_image->signal_connect(
|
||||||
|
'clicked',
|
||||||
|
sub {
|
||||||
|
my $dialog = Gtk4::FileDialog->new;
|
||||||
|
$dialog->open(
|
||||||
|
$window, undef,
|
||||||
|
sub( $source, $res, $data ) {
|
||||||
|
eval {
|
||||||
|
my $file = $dialog->open_finish($res);
|
||||||
|
my $buffer = $entry_label->get_buffer;
|
||||||
|
my $label = $buffer->get_text;
|
||||||
|
$file = $file->get_path;
|
||||||
|
if ( $file !~ /\.png$/ ) {
|
||||||
|
$self->write_to_parent->say("$file is not png.");
|
||||||
|
$self->write_to_parent->flush;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
eval {
|
||||||
|
$self->_file_format->add_png_image(
|
||||||
|
path($file)->slurp_raw, $label );
|
||||||
|
$buffer->set_text( '', -1 );
|
||||||
|
$self->_update_gallery_images( $window,
|
||||||
|
$images_scroll );
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
warn $@;
|
||||||
|
$self->write_to_parent->print($@);
|
||||||
|
$self->write_to_parent->flush;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if ($@) {
|
||||||
|
warn $@;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$self->_update_gallery_images( $window, $images_scroll );
|
||||||
|
$box->append($add_image);
|
||||||
|
$box->append($box_upper_menu);
|
||||||
|
$box->append($images_scroll);
|
||||||
|
$images_scroll->set_vexpand(1);
|
||||||
|
$window->set_child($box);
|
||||||
|
$window->present;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _update_gallery_images( $self, $window, $images_scroll ) {
|
||||||
|
my %image_hashes_to_label = $self->_file_format->image_hashes_to_label->%*;
|
||||||
|
my $gallery_box = Gtk4::Box->new( 'vertical', 10 );
|
||||||
|
my $i = 0;
|
||||||
|
my $row_image;
|
||||||
|
for my $hash ( sort { $a cmp $b } keys %image_hashes_to_label ) {
|
||||||
|
my @labels = $image_hashes_to_label{$hash}->@*;
|
||||||
|
if ( scalar @labels == 0 ) {
|
||||||
|
$row_image = $self->_get_row_image( $i, $gallery_box, $row_image );
|
||||||
|
my $picture = $self->_create_gallery_image($hash);
|
||||||
|
my $box_image = Gtk4::Box->new( 'vertical', 1 );
|
||||||
|
$box_image->set_property( 'width-request', 180 );
|
||||||
|
my $copy = Gtk4::Button->new_with_label('Copy to clipboard');
|
||||||
|
my $copy_gd = Gtk4::Button->new_with_label('Copy GD to clipboard');
|
||||||
|
$copy->signal_connect(
|
||||||
|
'clicked',
|
||||||
|
sub {
|
||||||
|
$self->_set_clipboard("\$exd->get_image('$hash')");
|
||||||
|
$window->close;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$copy_gd->signal_connect(
|
||||||
|
'clicked',
|
||||||
|
sub {
|
||||||
|
$self->_set_clipboard("\$exd->get_image_gd('$hash')");
|
||||||
|
$window->close;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$box_image->append($picture);
|
||||||
|
my $scroll = Gtk4::ScrolledWindow->new;
|
||||||
|
$scroll->set_child( Gtk4::Label->new($hash) );
|
||||||
|
$box_image->append($scroll);
|
||||||
|
$box_image->append($copy);
|
||||||
|
$box_image->append($copy_gd);
|
||||||
|
$row_image->append($box_image);
|
||||||
|
$i++;
|
||||||
|
next;
|
||||||
|
}
|
||||||
|
for my $label (@labels) {
|
||||||
|
$row_image = $self->_get_row_image( $i, $gallery_box, $row_image );
|
||||||
|
my $picture = $self->_create_gallery_image($hash);
|
||||||
|
my $copy = Gtk4::Button->new_with_label('Copy to clipboard');
|
||||||
|
my $copy_gd = Gtk4::Button->new_with_label('Copy GD to clipboard');
|
||||||
|
my $box_image = Gtk4::Box->new( 'vertical', 1 );
|
||||||
|
$box_image->set_property( 'width-request', 180 );
|
||||||
|
$copy->signal_connect(
|
||||||
|
'clicked',
|
||||||
|
sub {
|
||||||
|
$self->_set_clipboard("\$exd->get_image_from_label('$label')");
|
||||||
|
$window->close;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$copy_gd->signal_connect(
|
||||||
|
'clicked',
|
||||||
|
sub {
|
||||||
|
$self->_set_clipboard("\$exd->get_image_gd_from_label('$label')");
|
||||||
|
$window->close;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
$box_image->append($picture);
|
||||||
|
my $scroll = Gtk4::ScrolledWindow->new;
|
||||||
|
$scroll->set_child( Gtk4::Label->new($label) );
|
||||||
|
$box_image->append($scroll);
|
||||||
|
$box_image->append($copy);
|
||||||
|
$box_image->append($copy_gd);
|
||||||
|
$row_image->append($box_image);
|
||||||
|
$i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
$images_scroll->set_child($gallery_box);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _set_clipboard($self, $text) {
|
||||||
|
my $display = Gdk::Display::get_default();
|
||||||
|
my $clipboard = $display->get_clipboard;
|
||||||
|
my $wrapper =
|
||||||
|
Glib::Object::Introspection::GValueWrapper->new( 'Glib::String', $text );
|
||||||
|
$clipboard->set($wrapper);
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _get_row_image( $self, $i, $gallery_box, $row_image ) {
|
||||||
|
if ( $i % 3 == 0 ) {
|
||||||
|
$row_image = Gtk4::Box->new( 'horizontal', 10 );
|
||||||
|
$gallery_box->append($row_image);
|
||||||
|
}
|
||||||
|
return $row_image;
|
||||||
|
}
|
||||||
|
|
||||||
|
sub _create_gallery_image( $self, $hash ) {
|
||||||
|
my $picture = Gtk4::Picture->new;
|
||||||
|
my $image_file = $self->_file_format->get_image($hash);
|
||||||
|
$picture->set_property( 'width-request', 180 );
|
||||||
|
$picture->set_property( 'height-request', 180 );
|
||||||
|
$picture->set_content_fit('fill');
|
||||||
|
$picture->set_filename($image_file);
|
||||||
|
return $picture;
|
||||||
|
}
|
||||||
|
|
||||||
sub _activate($self) {
|
sub _activate($self) {
|
||||||
Glib::Timeout->add(
|
Glib::Timeout->add(
|
||||||
1000,
|
1000,
|
||||||
@ -347,12 +569,20 @@ sub _activate($self) {
|
|||||||
);
|
);
|
||||||
my $app = $self->_app;
|
my $app = $self->_app;
|
||||||
my $win = Gtk4::ApplicationWindow->new($app);
|
my $win = Gtk4::ApplicationWindow->new($app);
|
||||||
|
$win->set_show_menubar(1);
|
||||||
$self->window($win);
|
$self->window($win);
|
||||||
my $box_vertical = Gtk4::Box->new( 'vertical', 10 );
|
my $box_vertical = Gtk4::Box->new( 'vertical', 10 );
|
||||||
my $execute_log = Gtk4::TextView->new;
|
my $execute_log = Gtk4::TextView->new;
|
||||||
my $run_button = Gtk4::Button->new_with_label('Run');
|
my $run_button = Gtk4::Button->new_with_label('Run');
|
||||||
my $preview_button = Gtk4::Button->new_with_label('Preview');
|
my $preview_button = Gtk4::Button->new_with_label('Preview');
|
||||||
my $select_printer = Gtk4::Button->new_with_label('Select Printer');
|
my $select_printer = Gtk4::Button->new_with_label('Select Printer');
|
||||||
|
my $open_gallery = Gtk4::Button->new_with_label('Open image gallery');
|
||||||
|
$open_gallery->signal_connect(
|
||||||
|
'clicked',
|
||||||
|
sub {
|
||||||
|
$self->_open_gallery;
|
||||||
|
}
|
||||||
|
);
|
||||||
$select_printer->signal_connect(
|
$select_printer->signal_connect(
|
||||||
'clicked',
|
'clicked',
|
||||||
sub {
|
sub {
|
||||||
@ -388,6 +618,7 @@ sub _activate($self) {
|
|||||||
$box_buttons->append($run_button);
|
$box_buttons->append($run_button);
|
||||||
$box_buttons->append($preview_button);
|
$box_buttons->append($preview_button);
|
||||||
$box_buttons->append($select_printer);
|
$box_buttons->append($select_printer);
|
||||||
|
$box_buttons->append($open_gallery);
|
||||||
$self->_populate_editor_and_preview($box_vertical);
|
$self->_populate_editor_and_preview($box_vertical);
|
||||||
$box_vertical->append($box_buttons);
|
$box_vertical->append($box_buttons);
|
||||||
my $execute_log_window = Gtk4::ScrolledWindow->new;
|
my $execute_log_window = Gtk4::ScrolledWindow->new;
|
||||||
|
Loading…
Reference in New Issue
Block a user