Reimplementation of Dialogs

Cleanup code to integrate multithreading process for dialogs into the DialogToolkit (avoid poluting UserInterfaceManager and improves reliability)
This commit is contained in:
Bruno
2021-08-06 13:21:16 +02:00
parent 58afcacab9
commit d1b7073ff9
7 changed files with 409 additions and 196 deletions

View File

@@ -6,16 +6,17 @@
// because of 'zenity' access rights nightmare :( // because of 'zenity' access rights nightmare :(
// Thus this re-implementation of native GTK+ dialogs for linux // Thus this re-implementation of native GTK+ dialogs for linux
#include "defines.h"
#include "Settings.h"
#include "SystemToolkit.h"
#include "DialogToolkit.h"
#if defined(LINUX) #if defined(LINUX)
#define USE_TINYFILEDIALOG 0 #define USE_TINYFILEDIALOG 0
#else #else
#define USE_TINYFILEDIALOG 1 #define USE_TINYFILEDIALOG 1
#endif #endif
#include "defines.h"
#include "SystemToolkit.h"
#include "DialogToolkit.h"
#if USE_TINYFILEDIALOG #if USE_TINYFILEDIALOG
#include "tinyfiledialogs.h" #include "tinyfiledialogs.h"
#else #else
@@ -61,7 +62,140 @@ bool gtk_init()
} }
#endif #endif
std::string DialogToolkit::saveSessionFileDialog(const std::string &path)
// globals
const std::chrono::milliseconds timeout = std::chrono::milliseconds(4);
bool DialogToolkit::FileDialog::pending = false;
//
// FileDialog common functions
//
DialogToolkit::FileDialog::FileDialog(const std::string &name) : id_(name)
{
if ( Settings::application.dialogRecentFolder.count(id_) == 0 )
Settings::application.dialogRecentFolder[id_] = SystemToolkit::home_path();
}
bool DialogToolkit::FileDialog::closed()
{
if ( !promises_.empty() ) {
// check that file dialog thread finished
if (promises_.back().wait_for(timeout) == std::future_status::ready ) {
// get the filename from this file dialog
std::string string = promises_.back().get();
if (!string.empty()) {
// selected a filename
path_ = string;
// save path location
Settings::application.dialogRecentFolder[id_] = SystemToolkit::path_filename(string);
}
// done with this file dialog
promises_.pop_back();
pending = false;
return true;
}
}
return false;
}
//
// type specific implementations
//
std::string openImageFileDialog(const std::string &label, const std::string &path);
void DialogToolkit::OpenImageDialog::open()
{
if ( !pending && promises_.empty() ) {
promises_.emplace_back( std::async(std::launch::async, openImageFileDialog, id_,
Settings::application.dialogRecentFolder[id_]) );
pending = true;
}
}
std::string openSessionFileDialog(const std::string &label, const std::string &path);
void DialogToolkit::OpenSessionDialog::open()
{
if ( !pending && promises_.empty() ) {
promises_.emplace_back( std::async(std::launch::async, openSessionFileDialog, id_,
Settings::application.dialogRecentFolder[id_]) );
pending = true;
}
}
std::string openMediaFileDialog(const std::string &label, const std::string &path);
void DialogToolkit::OpenMediaDialog::open()
{
if ( !pending && promises_.empty() ) {
promises_.emplace_back( std::async(std::launch::async, openMediaFileDialog, id_,
Settings::application.dialogRecentFolder[id_]) );
pending = true;
}
}
std::string saveSessionFileDialog(const std::string &label, const std::string &path);
void DialogToolkit::SaveSessionDialog::open()
{
if ( !pending && promises_.empty() ) {
promises_.emplace_back( std::async(std::launch::async, saveSessionFileDialog, id_,
Settings::application.dialogRecentFolder[id_]) );
pending = true;
}
}
std::string openFolderDialog(const std::string &label, const std::string &path);
void DialogToolkit::OpenFolderDialog::open()
{
if ( !pending && promises_.empty() ) {
promises_.emplace_back( std::async(std::launch::async, openFolderDialog, id_,
Settings::application.dialogRecentFolder[id_]) );
pending = true;
}
}
std::list<std::string> selectImagesFileDialog(const std::string &label,const std::string &path);
void DialogToolkit::MultipleImagesDialog::open()
{
if ( !pending && promisedlist_.empty() ) {
promisedlist_.emplace_back( std::async(std::launch::async, selectImagesFileDialog, id_,
Settings::application.dialogRecentFolder[id_]) );
pending = true;
}
}
bool DialogToolkit::MultipleImagesDialog::closed()
{
if ( !promisedlist_.empty() ) {
// check that file dialog thread finished
if (promisedlist_.back().wait_for(timeout) == std::future_status::ready ) {
// get the filename from this file dialog
std::list<std::string> list = promisedlist_.back().get();
if (!list.empty()) {
// selected a filenames
pathlist_ = list;
path_ = list.front();
// save path location
Settings::application.dialogRecentFolder[id_] = SystemToolkit::path_filename(path_);
}
else {
pathlist_.clear();
path_.clear();
}
// done with this file dialog
promisedlist_.pop_back();
pending = false;
return true;
}
}
return false;
}
//
//
// CALLBACKS
//
//
std::string saveSessionFileDialog(const std::string &label, const std::string &path)
{ {
std::string filename = ""; std::string filename = "";
char const * save_pattern[1] = { "*.mix" }; char const * save_pattern[1] = { "*.mix" };
@@ -69,17 +203,17 @@ std::string DialogToolkit::saveSessionFileDialog(const std::string &path)
#if USE_TINYFILEDIALOG #if USE_TINYFILEDIALOG
char const * save_file_name; char const * save_file_name;
save_file_name = tinyfd_saveFileDialog( "Save a session file", path.c_str(), 1, save_pattern, "vimix session"); save_file_name = tinyfd_saveFileDialog( label.c_str(), path.c_str(), 1, save_pattern, "vimix session");
if (save_file_name) if (save_file_name)
filename = std::string(save_file_name); filename = std::string(save_file_name);
#else #else
if (!gtk_init()) { if (!gtk_init()) {
ErrorDialog("Could not initialize GTK+ for dialog"); DialogToolkit::ErrorDialog("Could not initialize GTK+ for dialog");
return filename; return filename;
} }
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Save Session File", NULL, GtkWidget *dialog = gtk_file_chooser_dialog_new( label.c_str(), NULL,
GTK_FILE_CHOOSER_ACTION_SAVE, GTK_FILE_CHOOSER_ACTION_SAVE,
"_Cancel", GTK_RESPONSE_CANCEL, "_Cancel", GTK_RESPONSE_CANCEL,
"_Save", GTK_RESPONSE_ACCEPT, NULL ); "_Save", GTK_RESPONSE_ACCEPT, NULL );
@@ -122,7 +256,7 @@ std::string DialogToolkit::saveSessionFileDialog(const std::string &path)
} }
std::string DialogToolkit::openSessionFileDialog(const std::string &path) std::string openSessionFileDialog(const std::string &label, const std::string &path)
{ {
std::string filename = ""; std::string filename = "";
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path(); std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
@@ -130,17 +264,17 @@ std::string DialogToolkit::openSessionFileDialog(const std::string &path)
#if USE_TINYFILEDIALOG #if USE_TINYFILEDIALOG
char const * open_file_name; char const * open_file_name;
open_file_name = tinyfd_openFileDialog( "Import a file", startpath.c_str(), 1, open_pattern, "vimix session", 0); open_file_name = tinyfd_openFileDialog( label.c_str(), startpath.c_str(), 1, open_pattern, "vimix session", 0);
if (open_file_name) if (open_file_name)
filename = std::string(open_file_name); filename = std::string(open_file_name);
#else #else
if (!gtk_init()) { if (!gtk_init()) {
ErrorDialog("Could not initialize GTK+ for dialog"); DialogToolkit::ErrorDialog("Could not initialize GTK+ for dialog");
return filename; return filename;
} }
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Open Session File", NULL, GtkWidget *dialog = gtk_file_chooser_dialog_new( label.c_str(), NULL,
GTK_FILE_CHOOSER_ACTION_OPEN, GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL, "_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT, NULL ); "_Open", GTK_RESPONSE_ACCEPT, NULL );
@@ -178,7 +312,7 @@ std::string DialogToolkit::openSessionFileDialog(const std::string &path)
} }
std::string DialogToolkit::openMediaFileDialog(const std::string &path) std::string openMediaFileDialog(const std::string &label, const std::string &path)
{ {
std::string filename = ""; std::string filename = "";
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path(); std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
@@ -190,18 +324,18 @@ std::string DialogToolkit::openMediaFileDialog(const std::string &path)
"*.gif", "*.tif", "*.svg" }; "*.gif", "*.tif", "*.svg" };
#if USE_TINYFILEDIALOG #if USE_TINYFILEDIALOG
char const * open_file_name; char const * open_file_name;
open_file_name = tinyfd_openFileDialog( "Open Media File", startpath.c_str(), 18, open_pattern, "All supported formats", 0); open_file_name = tinyfd_openFileDialog( label.c_str(), startpath.c_str(), 18, open_pattern, "All supported formats", 0);
if (open_file_name) if (open_file_name)
filename = std::string(open_file_name); filename = std::string(open_file_name);
#else #else
if (!gtk_init()) { if (!gtk_init()) {
ErrorDialog("Could not initialize GTK+ for dialog"); DialogToolkit::ErrorDialog("Could not initialize GTK+ for dialog");
return filename; return filename;
} }
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Open Media File", NULL, GtkWidget *dialog = gtk_file_chooser_dialog_new( label.c_str(), NULL,
GTK_FILE_CHOOSER_ACTION_OPEN, GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL, "_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT, NULL ); "_Open", GTK_RESPONSE_ACCEPT, NULL );
@@ -238,24 +372,81 @@ std::string DialogToolkit::openMediaFileDialog(const std::string &path)
return filename; return filename;
} }
std::string DialogToolkit::openFolderDialog(const std::string &path)
std::string openImageFileDialog(const std::string &label, const std::string &path)
{
std::string filename = "";
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
char const * open_pattern[2] = { "*.jpg", "*.png"};
#if USE_TINYFILEDIALOG
char const * open_file_name;
open_file_name = tinyfd_openFileDialog( label.c_str(), startpath.c_str(), 2, open_pattern, "Image JPG or PNG", 0);
if (open_file_name)
filename = std::string(open_file_name);
#else
if (!gtk_init()) {
DialogToolkit::ErrorDialog("Could not initialize GTK+ for dialog");
return filename;
}
GtkWidget *dialog = gtk_file_chooser_dialog_new( label.c_str(), NULL,
GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT, NULL );
// set file filters
add_filter_file_dialog(dialog, 2, open_pattern, "Image JPG or PNG");
add_filter_any_file_dialog(dialog);
// Set the default path
gtk_file_chooser_set_current_folder( GTK_FILE_CHOOSER(dialog), startpath.c_str() );
// ensure front and centered
gtk_window_set_keep_above( GTK_WINDOW(dialog), TRUE );
if (window_x > 0 && window_y > 0)
gtk_window_move( GTK_WINDOW(dialog), window_x, window_y);
// display and get filename
if ( gtk_dialog_run( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT ) {
char *open_file_name = gtk_file_chooser_get_filename( GTK_FILE_CHOOSER(dialog) );
if (open_file_name)
filename = std::string(open_file_name);
g_free( open_file_name );
}
// remember position
gtk_window_get_position( GTK_WINDOW(dialog), &window_x, &window_y);
// done
gtk_widget_destroy(dialog);
wait_for_event();
#endif
return filename;
}
std::string openFolderDialog(const std::string &label, const std::string &path)
{ {
std::string foldername = ""; std::string foldername = "";
std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path(); std::string startpath = SystemToolkit::file_exists(path) ? path : SystemToolkit::home_path();
#if USE_TINYFILEDIALOG #if USE_TINYFILEDIALOG
char const * open_folder_name; char const * open_folder_name;
open_folder_name = tinyfd_selectFolderDialog("Select folder", startpath.c_str()); open_folder_name = tinyfd_selectFolderDialog(label.c_str(), startpath.c_str());
if (open_folder_name) if (open_folder_name)
foldername = std::string(open_folder_name); foldername = std::string(open_folder_name);
#else #else
if (!gtk_init()) { if (!gtk_init()) {
ErrorDialog("Could not initialize GTK+ for dialog"); DialogToolkit::ErrorDialog("Could not initialize GTK+ for dialog");
return foldername; return foldername;
} }
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Select folder", NULL, GtkWidget *dialog = gtk_file_chooser_dialog_new( label.c_str(), NULL,
GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER, GTK_FILE_CHOOSER_ACTION_SELECT_FOLDER,
"_Cancel", GTK_RESPONSE_CANCEL, "_Cancel", GTK_RESPONSE_CANCEL,
"_Select", GTK_RESPONSE_ACCEPT, NULL ); "_Select", GTK_RESPONSE_ACCEPT, NULL );
@@ -290,7 +481,7 @@ std::string DialogToolkit::openFolderDialog(const std::string &path)
} }
std::list<std::string> DialogToolkit::selectImagesFileDialog(const std::string &path) std::list<std::string> selectImagesFileDialog(const std::string &label,const std::string &path)
{ {
std::list<std::string> files; std::list<std::string> files;
@@ -299,7 +490,7 @@ std::list<std::string> DialogToolkit::selectImagesFileDialog(const std::string &
#if USE_TINYFILEDIALOG #if USE_TINYFILEDIALOG
char const * open_file_names; char const * open_file_names;
open_file_names = tinyfd_openFileDialog( "Select images", startpath.c_str(), 6, open_pattern, "Images", 1); open_file_names = tinyfd_openFileDialog(label.c_str(), startpath.c_str(), 6, open_pattern, "Images", 1);
if (open_file_names) { if (open_file_names) {
@@ -325,11 +516,11 @@ std::list<std::string> DialogToolkit::selectImagesFileDialog(const std::string &
#else #else
if (!gtk_init()) { if (!gtk_init()) {
ErrorDialog("Could not initialize GTK+ for dialog"); DialogToolkit::ErrorDialog("Could not initialize GTK+ for dialog");
return files; return files;
} }
GtkWidget *dialog = gtk_file_chooser_dialog_new( "Select images", NULL, GtkWidget *dialog = gtk_file_chooser_dialog_new( label.c_str(), NULL,
GTK_FILE_CHOOSER_ACTION_OPEN, GTK_FILE_CHOOSER_ACTION_OPEN,
"_Cancel", GTK_RESPONSE_CANCEL, "_Cancel", GTK_RESPONSE_CANCEL,
"_Open", GTK_RESPONSE_ACCEPT, NULL ); "_Open", GTK_RESPONSE_ACCEPT, NULL );

View File

@@ -3,23 +3,80 @@
#include <string> #include <string>
#include <list> #include <list>
#include <vector>
#include <thread>
#include <future>
namespace DialogToolkit namespace DialogToolkit
{ {
std::string saveSessionFileDialog(const std::string &path);
std::string openSessionFileDialog(const std::string &path);
std::string openMediaFileDialog(const std::string &path);
std::string openFolderDialog(const std::string &path);
std::list<std::string> selectImagesFileDialog(const std::string &path);
void ErrorDialog(const char* message); void ErrorDialog(const char* message);
class FileDialog
{
protected:
std::string id_;
std::string directory_;
std::string path_;
std::vector< std::future<std::string> >promises_;
public:
FileDialog(const std::string &name);
virtual void open() = 0;
virtual bool closed();
inline std::string path() const { return path_; }
static bool pending;
};
class OpenImageDialog : public FileDialog
{
public:
OpenImageDialog(const std::string &name) : FileDialog(name) {}
void open();
};
class OpenSessionDialog : public FileDialog
{
public:
OpenSessionDialog(const std::string &name) : FileDialog(name) {}
void open();
};
class OpenMediaDialog : public FileDialog
{
public:
OpenMediaDialog(const std::string &name) : FileDialog(name) {}
void open();
};
class SaveSessionDialog : public FileDialog
{
public:
SaveSessionDialog(const std::string &name) : FileDialog(name) {}
void open();
};
class OpenFolderDialog : public FileDialog
{
public:
OpenFolderDialog(const std::string &name) : FileDialog(name) {}
void open();
};
class MultipleImagesDialog : public FileDialog
{
std::list<std::string> pathlist_;
std::vector< std::future< std::list<std::string> > > promisedlist_;
public:
MultipleImagesDialog(const std::string &name) : FileDialog(name) {}
void open();
bool closed() override;
inline std::list<std::string> images() const { return pathlist_; }
};
} }

View File

@@ -238,7 +238,6 @@ Source * Mixer::createSourceFile(const std::string &path)
// remember in recent media // remember in recent media
Settings::application.recentImport.push(path); Settings::application.recentImport.push(path);
Settings::application.recentImport.path = SystemToolkit::path_filename(path);
// propose a new name based on uri // propose a new name based on uri
s->setName(SystemToolkit::base_filename(path)); s->setName(SystemToolkit::base_filename(path));
@@ -269,9 +268,6 @@ Source * Mixer::createSourceMultifile(const std::list<std::string> &list_files,
mfs->setSequence(sequence, fps); mfs->setSequence(sequence, fps);
s = mfs; s = mfs;
// remember in recent media
Settings::application.recentImport.path = SystemToolkit::path_filename(list_files.front());
// propose a new name // propose a new name
s->setName( SystemToolkit::base_filename( BaseToolkit::common_prefix(list_files) ) ); s->setName( SystemToolkit::base_filename( BaseToolkit::common_prefix(list_files) ) );
} }

View File

@@ -173,13 +173,14 @@ void Settings::Save()
{ {
XMLElement *recent = xmlDoc.NewElement( "Recent" ); XMLElement *recent = xmlDoc.NewElement( "Recent" );
// recent session filenames
XMLElement *recentsession = xmlDoc.NewElement( "Session" ); XMLElement *recentsession = xmlDoc.NewElement( "Session" );
recentsession->SetAttribute("path", application.recentSessions.path.c_str()); recentsession->SetAttribute("path", application.recentSessions.path.c_str());
recentsession->SetAttribute("autoload", application.recentSessions.load_at_start); recentsession->SetAttribute("autoload", application.recentSessions.load_at_start);
recentsession->SetAttribute("autosave", application.recentSessions.save_on_exit); recentsession->SetAttribute("autosave", application.recentSessions.save_on_exit);
recentsession->SetAttribute("valid", application.recentSessions.front_is_valid); recentsession->SetAttribute("valid", application.recentSessions.front_is_valid);
for(auto it = application.recentSessions.filenames.begin(); for(auto it = application.recentSessions.filenames.cbegin();
it != application.recentSessions.filenames.end(); ++it) { it != application.recentSessions.filenames.cend(); ++it) {
XMLElement *fileNode = xmlDoc.NewElement("path"); XMLElement *fileNode = xmlDoc.NewElement("path");
XMLText *text = xmlDoc.NewText( (*it).c_str() ); XMLText *text = xmlDoc.NewText( (*it).c_str() );
fileNode->InsertEndChild( text ); fileNode->InsertEndChild( text );
@@ -187,9 +188,10 @@ void Settings::Save()
}; };
recent->InsertEndChild(recentsession); recent->InsertEndChild(recentsession);
// recent session folders
XMLElement *recentfolder = xmlDoc.NewElement( "Folder" ); XMLElement *recentfolder = xmlDoc.NewElement( "Folder" );
for(auto it = application.recentFolders.filenames.begin(); for(auto it = application.recentFolders.filenames.cbegin();
it != application.recentFolders.filenames.end(); ++it) { it != application.recentFolders.filenames.cend(); ++it) {
XMLElement *fileNode = xmlDoc.NewElement("path"); XMLElement *fileNode = xmlDoc.NewElement("path");
XMLText *text = xmlDoc.NewText( (*it).c_str() ); XMLText *text = xmlDoc.NewText( (*it).c_str() );
fileNode->InsertEndChild( text ); fileNode->InsertEndChild( text );
@@ -197,10 +199,11 @@ void Settings::Save()
}; };
recent->InsertEndChild(recentfolder); recent->InsertEndChild(recentfolder);
// recent media uri
XMLElement *recentmedia = xmlDoc.NewElement( "Import" ); XMLElement *recentmedia = xmlDoc.NewElement( "Import" );
recentmedia->SetAttribute("path", application.recentImport.path.c_str()); recentmedia->SetAttribute("path", application.recentImport.path.c_str());
for(auto it = application.recentImport.filenames.begin(); for(auto it = application.recentImport.filenames.cbegin();
it != application.recentImport.filenames.end(); ++it) { it != application.recentImport.filenames.cend(); ++it) {
XMLElement *fileNode = xmlDoc.NewElement("path"); XMLElement *fileNode = xmlDoc.NewElement("path");
XMLText *text = xmlDoc.NewText( (*it).c_str() ); XMLText *text = xmlDoc.NewText( (*it).c_str() );
fileNode->InsertEndChild( text ); fileNode->InsertEndChild( text );
@@ -208,10 +211,21 @@ void Settings::Save()
} }
recent->InsertEndChild(recentmedia); recent->InsertEndChild(recentmedia);
// recent dialog path
XMLElement *recentdialogpath = xmlDoc.NewElement( "Dialog" );
for(auto it = application.dialogRecentFolder.cbegin();
it != application.dialogRecentFolder.cend(); ++it) {
XMLElement *pathNode = xmlDoc.NewElement("path");
pathNode->SetAttribute("label", (*it).first.c_str() );
XMLText *text = xmlDoc.NewText( (*it).second.c_str() );
pathNode->InsertEndChild( text );
recentdialogpath->InsertFirstChild(pathNode);
}
recent->InsertEndChild(recentdialogpath);
pRoot->InsertEndChild(recent); pRoot->InsertEndChild(recent);
} }
// First save : create filename // First save : create filename
if (settingsFilename.empty()) if (settingsFilename.empty())
settingsFilename = SystemToolkit::full_filename(SystemToolkit::settings_path(), APP_SETTINGS); settingsFilename = SystemToolkit::full_filename(SystemToolkit::settings_path(), APP_SETTINGS);
@@ -459,6 +473,21 @@ void Settings::Load()
application.recentImport.push( std::string (p) ); application.recentImport.push( std::string (p) );
} }
} }
// recent dialog path
XMLElement * pDialog = pElement->FirstChildElement("Dialog");
if (pDialog)
{
application.dialogRecentFolder.clear();
XMLElement* path = pDialog->FirstChildElement("path");
for( ; path ; path = path->NextSiblingElement())
{
const char *l = path->Attribute("label");
const char *p = path->GetText();
if (l && p)
application.dialogRecentFolder[ std::string(l)] = std::string (p);
}
}
} }
} }

View File

@@ -70,7 +70,6 @@ struct ViewConfig
}; };
#define RECORD_MAX_TIMEOUT 301000
struct RecordConfig struct RecordConfig
{ {
@@ -211,6 +210,7 @@ struct Application
History recentSessions; History recentSessions;
History recentFolders; History recentFolders;
History recentImport; History recentImport;
std::map< std::string, std::string > dialogRecentFolder;
Application() : fresh_start(false), instance_id(0), name(APP_NAME), executable(APP_NAME) { Application() : fresh_start(false), instance_id(0), name(APP_NAME), executable(APP_NAME) {
scale = 1.f; scale = 1.f;

View File

@@ -81,17 +81,8 @@ void ShowSandbox(bool* p_open);
void SetMouseCursor(ImVec2 mousepos, View::Cursor c = View::Cursor()); void SetMouseCursor(ImVec2 mousepos, View::Cursor c = View::Cursor());
void SetNextWindowVisible(ImVec2 pos, ImVec2 size, float margin = 180.f); void SetNextWindowVisible(ImVec2 pos, ImVec2 size, float margin = 180.f);
// static objects for multithreaded file dialog // globals
const std::chrono::milliseconds timeout = std::chrono::milliseconds(4); const std::chrono::milliseconds timeout = std::chrono::milliseconds(4);
static std::atomic<bool> fileDialogPending_ = false;
std::vector< std::future<std::string> > saveSessionFileDialogs;
std::vector< std::future<std::string> > openSessionFileDialogs;
std::vector< std::future<std::string> > importSessionFileDialogs;
std::vector< std::future<std::string> > fileImportFileDialogs;
std::vector< std::future<std::string> > recentFolderFileDialogs;
std::vector< std::future<std::string> > recordFolderFileDialogs;
std::vector< std::future<FrameGrabber *> > _video_recorders; std::vector< std::future<FrameGrabber *> > _video_recorders;
FrameGrabber *delayTrigger(FrameGrabber *g, std::chrono::milliseconds delay) { FrameGrabber *delayTrigger(FrameGrabber *g, std::chrono::milliseconds delay) {
std::this_thread::sleep_for (delay); std::this_thread::sleep_for (delay);
@@ -119,6 +110,10 @@ UserInterface::UserInterface()
#if defined(LINUX) #if defined(LINUX)
webcam_emulator_ = nullptr; webcam_emulator_ = nullptr;
#endif #endif
sessionopendialog = nullptr;
sessionimportdialog = nullptr;
sessionsavedialog = nullptr;
} }
bool UserInterface::Init() bool UserInterface::Init()
@@ -181,6 +176,11 @@ bool UserInterface::Init()
std::sprintf(inifilepath, "%s", inifile.c_str() ); std::sprintf(inifilepath, "%s", inifile.c_str() );
io.IniFilename = inifilepath; io.IniFilename = inifilepath;
// init dialogs
sessionopendialog = new DialogToolkit::OpenSessionDialog("Open Session");
sessionsavedialog = new DialogToolkit::SaveSessionDialog("Save Session");
sessionimportdialog = new DialogToolkit::OpenSessionDialog("Import Session") ;
return true; return true;
} }
@@ -664,21 +664,18 @@ void UserInterface::handleMouse()
void UserInterface::selectSaveFilename() void UserInterface::selectSaveFilename()
{ {
// launch file dialog to select a session filename to save if (sessionsavedialog)
if ( saveSessionFileDialogs.empty()) { sessionsavedialog->open();
saveSessionFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::saveSessionFileDialog, Settings::application.recentSessions.path) );
fileDialogPending_ = true;
}
navigator.hidePannel(); navigator.hidePannel();
} }
void UserInterface::selectOpenFilename() void UserInterface::selectOpenFilename()
{ {
// launch file dialog to select a session filename to open // launch file dialog to select a session filename to open
if ( openSessionFileDialogs.empty()) { if (sessionopendialog)
openSessionFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::openSessionFileDialog, Settings::application.recentSessions.path) ); sessionopendialog->open();
fileDialogPending_ = true;
}
navigator.hidePannel(); navigator.hidePannel();
} }
@@ -694,54 +691,18 @@ void UserInterface::NewFrame()
handleMouse(); handleMouse();
handleScreenshot(); handleScreenshot();
// handle FileDialog // handle FileDialogs
if (sessionopendialog && sessionopendialog->closed() && !sessionopendialog->path().empty())
Mixer::manager().open(sessionopendialog->path());
// if a file dialog future was registered if (sessionimportdialog && sessionimportdialog->closed() && !sessionimportdialog->path().empty())
if ( !openSessionFileDialogs.empty() ) { Mixer::manager().import(sessionimportdialog->path());
// check that file dialog thread finished
if (openSessionFileDialogs.back().wait_for(timeout) == std::future_status::ready ) { if (sessionsavedialog && sessionsavedialog->closed() && !sessionsavedialog->path().empty())
// get the filename from this file dialog Mixer::manager().saveas(sessionsavedialog->path());
std::string open_filename = openSessionFileDialogs.back().get();
if (!open_filename.empty()) {
Mixer::manager().open(open_filename);
Settings::application.recentSessions.path = SystemToolkit::path_filename(open_filename);
}
// done with this file dialog
openSessionFileDialogs.pop_back();
fileDialogPending_ = false;
}
}
if ( !importSessionFileDialogs.empty() ) {
// check that file dialog thread finished
if (importSessionFileDialogs.back().wait_for(timeout) == std::future_status::ready ) {
// get the filename from this file dialog
std::string open_filename = importSessionFileDialogs.back().get();
if (!open_filename.empty()) {
Mixer::manager().import(open_filename);
Settings::application.recentSessions.path = SystemToolkit::path_filename(open_filename);
}
// done with this file dialog
importSessionFileDialogs.pop_back();
fileDialogPending_ = false;
}
}
if ( !saveSessionFileDialogs.empty() ) {
// check that file dialog thread finished
if (saveSessionFileDialogs.back().wait_for(timeout) == std::future_status::ready ) {
// get the filename from this file dialog
std::string save_filename = saveSessionFileDialogs.back().get();
if (!save_filename.empty()) {
Mixer::manager().saveas(save_filename);
Settings::application.recentSessions.path = SystemToolkit::path_filename(save_filename);
}
// done with this file dialog
saveSessionFileDialogs.pop_back();
fileDialogPending_ = false;
}
}
// overlay to ensure file dialog is modal // overlay to ensure file dialog is modal
if (fileDialogPending_){ if (DialogToolkit::FileDialog::pending){
ImGui::OpenPopup("Busy"); ImGui::OpenPopup("Busy");
if (ImGui::BeginPopupModal("Busy", NULL, ImGuiWindowFlags_AlwaysAutoResize)) if (ImGui::BeginPopupModal("Busy", NULL, ImGuiWindowFlags_AlwaysAutoResize))
{ {
@@ -898,10 +859,11 @@ void UserInterface::showMenuFile()
if (ImGui::MenuItem( ICON_FA_FILE_EXPORT " Import")) { if (ImGui::MenuItem( ICON_FA_FILE_EXPORT " Import")) {
// launch file dialog to open a session file // launch file dialog to open a session file
if ( importSessionFileDialogs.empty()) { sessionimportdialog->open();
importSessionFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::openSessionFileDialog, Settings::application.recentSessions.path) ); // if ( importSessionFileDialogs.empty()) {
fileDialogPending_ = true; // importSessionFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::openSessionFileDialog, Settings::application.recentSessions.path) );
} // fileDialogPending_ = true;
// }
navigator.hidePannel(); navigator.hidePannel();
} }
if (ImGui::MenuItem( ICON_FA_FILE_DOWNLOAD " Save", CTRL_MOD "S")) { if (ImGui::MenuItem( ICON_FA_FILE_DOWNLOAD " Save", CTRL_MOD "S")) {
@@ -1029,7 +991,11 @@ void UserInterface::RenderPreview()
bool openInitializeSystemLoopback = false; bool openInitializeSystemLoopback = false;
#endif #endif
struct CustomConstraints // Helper functions for aspect-ratio constraints // recording location
static DialogToolkit::OpenFolderDialog recordFolderDialog("Recording Location");
// Helper functions for aspect-ratio constraints
struct CustomConstraints
{ {
static void AspectRatio(ImGuiSizeCallbackData* data) { static void AspectRatio(ImGuiSizeCallbackData* data) {
float *ar = (float*) data->UserData; float *ar = (float*) data->UserData;
@@ -1063,16 +1029,9 @@ void UserInterface::RenderPreview()
preview_window_size = ImGui::GetWindowSize(); preview_window_size = ImGui::GetWindowSize();
// return from thread for folder openning // return from thread for folder openning
if ( !recordFolderFileDialogs.empty() ) { if (recordFolderDialog.closed() && !recordFolderDialog.path().empty())
// check that file dialog thread finished // get the folder from this file dialog
if (recordFolderFileDialogs.back().wait_for(timeout) == std::future_status::ready ) { Settings::application.record.path = recordFolderDialog.path();
// get the folder from this file dialog
Settings::application.record.path = recordFolderFileDialogs.back().get();
// done with this file dialog
recordFolderFileDialogs.pop_back();
fileDialogPending_ = false;
}
}
// menu (no title bar) // menu (no title bar)
if (ImGui::BeginMenuBar()) if (ImGui::BeginMenuBar())
@@ -1169,12 +1128,8 @@ void UserInterface::RenderPreview()
int selected_path = 0; int selected_path = 0;
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
ImGui::Combo("Path", &selected_path, name_path, 4); ImGui::Combo("Path", &selected_path, name_path, 4);
if (selected_path > 2) { if (selected_path > 2)
if (recordFolderFileDialogs.empty()) { recordFolderDialog.open();
recordFolderFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::openFolderDialog, Settings::application.record.path) );
fileDialogPending_ = true;
}
}
else if (selected_path > 1) else if (selected_path > 1)
Settings::application.record.path = SystemToolkit::path_filename( Mixer::manager().session()->filename() ); Settings::application.record.path = SystemToolkit::path_filename( Mixer::manager().session()->filename() );
else if (selected_path > 0) else if (selected_path > 0)
@@ -3709,36 +3664,27 @@ void Navigator::RenderNewPannel()
// File Source creation // File Source creation
if (Settings::application.source.new_type == 0) { if (Settings::application.source.new_type == 0) {
static DialogToolkit::OpenMediaDialog fileimportdialog("Open Media");
// clic button to load file // clic button to load file
if ( ImGui::Button( ICON_FA_FILE_EXPORT " Open media", ImVec2(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, 0)) ) { if ( ImGui::Button( ICON_FA_FILE_EXPORT " Open media", ImVec2(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, 0)) )
// launch async call to file dialog and get its future. fileimportdialog.open();
if (fileImportFileDialogs.empty()) {
fileImportFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::openMediaFileDialog, Settings::application.recentImport.path) );
fileDialogPending_ = true;
}
}
// Indication // Indication
ImGui::SameLine(); ImGui::SameLine();
ImGuiToolkit::HelpMarker("Create a source from a file:\n- video (*.mpg, *mov, *.avi, etc.)\n- image (*.jpg, *.png, etc.)\n- vector graphics (*.svg)\n- vimix session (*.mix)\n\n(Equivalent to dropping the file in the workspace)"); ImGuiToolkit::HelpMarker("Create a source from a file:\n- video (*.mpg, *mov, *.avi, etc.)\n- image (*.jpg, *.png, etc.)\n- vector graphics (*.svg)\n- vimix session (*.mix)\n\n(Equivalent to dropping the file in the workspace)");
// if a file dialog future was registered // get media file if dialog finished
if ( !fileImportFileDialogs.empty() ) { if (fileimportdialog.closed()){
// check that file dialog thread finished // get the filename from this file dialog
if (fileImportFileDialogs.back().wait_for(timeout) == std::future_status::ready ) { std::string open_filename = fileimportdialog.path();
// get the filename from this file dialog // create a source with this file
std::string open_filename = fileImportFileDialogs.back().get(); if (open_filename.empty()) {
// done with this file dialog Log::Notify("No file selected.");
fileImportFileDialogs.pop_back(); } else {
fileDialogPending_ = false; std::string label = BaseToolkit::transliterate( open_filename );
// create a source with this file label = BaseToolkit::trunc_string(label, 35);
if (open_filename.empty()) { new_source_preview_.setSource( Mixer::manager().createSourceFile(open_filename), label);
Log::Notify("No file selected.");
} else {
std::string label = BaseToolkit::transliterate( open_filename );
label = BaseToolkit::trunc_string(label, 35);
new_source_preview_.setSource( Mixer::manager().createSourceFile(open_filename), label);
}
} }
} }
@@ -3765,15 +3711,12 @@ void Navigator::RenderNewPannel()
else if (Settings::application.source.new_type == 1){ else if (Settings::application.source.new_type == 1){
bool update_new_source = false; bool update_new_source = false;
static std::vector< std::future< std::list<std::string> > > _selectedImagesFileDialogs; static DialogToolkit::MultipleImagesDialog _selectImagesDialog("Select Images");
// clic button to load file // clic button to load file
if ( ImGui::Button( ICON_FA_IMAGES " Open images", ImVec2(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, 0)) ) { if ( ImGui::Button( ICON_FA_IMAGES " Open images", ImVec2(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, 0)) ) {
_selectedFiles.clear(); _selectedFiles.clear();
if (_selectedImagesFileDialogs.empty()) { _selectImagesDialog.open();
_selectedImagesFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::selectImagesFileDialog, Settings::application.recentImport.path) );
fileDialogPending_ = true;
}
} }
// Indication // Indication
@@ -3781,20 +3724,12 @@ void Navigator::RenderNewPannel()
ImGuiToolkit::HelpMarker("Create a source from a sequence of numbered images."); ImGuiToolkit::HelpMarker("Create a source from a sequence of numbered images.");
// return from thread for folder openning // return from thread for folder openning
if ( !_selectedImagesFileDialogs.empty() ) { if (_selectImagesDialog.closed()) {
// check that file dialog thread finished _selectedFiles = _selectImagesDialog.images();
if (_selectedImagesFileDialogs.back().wait_for(timeout) == std::future_status::ready ) { if (_selectedFiles.empty())
// get the filenames from this file dialog Log::Notify("No file selected.");
_selectedFiles = _selectedImagesFileDialogs.back().get(); // ask to reload the preview
if (_selectedFiles.empty()) { update_new_source = true;
Log::Notify("No file selected.");
}
// done with this file dialog
_selectedImagesFileDialogs.pop_back();
fileDialogPending_ = false;
// ask to reload the preview
update_new_source = true;
}
} }
// multiple files selected // multiple files selected
@@ -3980,6 +3915,7 @@ void Navigator::RenderMainPannelVimix()
ImGui::Text("Sessions"); ImGui::Text("Sessions");
static bool selection_session_mode_changed = true; static bool selection_session_mode_changed = true;
static int selection_session_mode = 0; static int selection_session_mode = 0;
static DialogToolkit::OpenFolderDialog customFolder("Open Folder");
// Show combo box of quick selection modes // Show combo box of quick selection modes
ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN);
@@ -4004,32 +3940,17 @@ void Navigator::RenderMainPannelVimix()
} }
} }
// Option 2 : add a folder // Option 2 : add a folder
if (ImGui::Selectable( ICON_FA_FOLDER_PLUS " Add Folder") ){ if (ImGui::Selectable( ICON_FA_FOLDER_PLUS " Add Folder") )
if (recentFolderFileDialogs.empty()) { customFolder.open();
recentFolderFileDialogs.emplace_back( std::async(std::launch::async, DialogToolkit::openFolderDialog, Settings::application.recentFolders.path) );
fileDialogPending_ = true;
}
}
ImGui::EndCombo(); ImGui::EndCombo();
} }
// return from thread for folder openning // return from thread for folder openning
if ( !recentFolderFileDialogs.empty() ) { if (customFolder.closed() && !customFolder.path().empty()) {
// check that file dialog thread finished Settings::application.recentFolders.push(customFolder.path());
if (recentFolderFileDialogs.back().wait_for(timeout) == std::future_status::ready ) { Settings::application.recentFolders.path.assign(customFolder.path());
// get the filename from this file dialog selection_session_mode = 1;
std::string foldername = recentFolderFileDialogs.back().get(); selection_session_mode_changed = true;
if (!foldername.empty()) {
Settings::application.recentFolders.push(foldername);
Settings::application.recentFolders.path.assign(foldername);
selection_session_mode = 1;
selection_session_mode_changed = true;
}
// done with this file dialog
recentFolderFileDialogs.pop_back();
fileDialogPending_ = false;
}
} }
// icon to clear list // icon to clear list
@@ -4949,6 +4870,19 @@ void ShowSandbox(bool* p_open)
ImGui::Text("Testing sandox"); ImGui::Text("Testing sandox");
ImGui::Separator(); ImGui::Separator();
ImGui::Text("IMAGE of Font");
ImGuiToolkit::ImageGlyph(ImGuiToolkit::FONT_DEFAULT, 'v');
ImGui::SameLine();
ImGuiToolkit::ImageGlyph(ImGuiToolkit::FONT_BOLD, 'i');
ImGui::SameLine();
ImGuiToolkit::ImageGlyph(ImGuiToolkit::FONT_ITALIC, 'm');
ImGui::SameLine();
ImGuiToolkit::ImageGlyph(ImGuiToolkit::FONT_MONO, 'i');
ImGui::SameLine();
ImGuiToolkit::ImageGlyph(ImGuiToolkit::FONT_LARGE, 'x');
ImGui::Separator();
ImGui::Text("Source list"); ImGui::Text("Source list");
Session *se = Mixer::manager().session(); Session *se = Mixer::manager().session();
for (auto sit = se->begin(); sit != se->end(); ++sit) { for (auto sit = se->begin(); sit != se->end(); ++sit) {

View File

@@ -12,6 +12,7 @@
#include "SourceList.h" #include "SourceList.h"
#include "InfoVisitor.h" #include "InfoVisitor.h"
#include "DialogToolkit.h"
struct ImVec2; struct ImVec2;
class MediaPlayer; class MediaPlayer;
@@ -183,6 +184,11 @@ class UserInterface
FrameGrabber *webcam_emulator_; FrameGrabber *webcam_emulator_;
#endif #endif
// Dialogs
DialogToolkit::OpenSessionDialog *sessionopendialog;
DialogToolkit::OpenSessionDialog *sessionimportdialog;
DialogToolkit::SaveSessionDialog *sessionsavedialog;
// Private Constructor // Private Constructor
UserInterface(); UserInterface();
UserInterface(UserInterface const& copy) = delete; UserInterface(UserInterface const& copy) = delete;