From 3454b0cbcea146ee3d5790189adea6447837eb3b Mon Sep 17 00:00:00 2001
From: Tristan Walter <twalter@orn.mpg.de>
Date: Wed, 28 Oct 2020 16:28:02 +0100
Subject: [PATCH] * avoiding some deadlocks * adding checkboxes to opening
 dialog * renaming callbacks * callback also on empty path * hiding file
 selection when switching tabs * video info for MP4s * smaller fonts

---
 .../src/commons/common/gui/FileChooser.cpp    | 82 ++++++++++++-------
 .../src/commons/common/gui/FileChooser.h      |  6 +-
 .../src/commons/common/gui/types/Checkbox.h   |  2 +-
 .../src/commons/common/gui/types/Textfield.h  |  2 +
 .../src/commons/common/misc/metastring.h      | 25 ++++++
 Application/src/tracker/VideoOpener.cpp       | 77 +++++++++++++----
 Application/src/tracker/VideoOpener.h         | 12 ++-
 Application/src/tracker/gui/gui.cpp           |  3 +-
 Application/src/tracker/main.cpp              |  2 +
 Application/src/tracker/tracking/Tracker.cpp  |  8 +-
 10 files changed, 165 insertions(+), 54 deletions(-)

diff --git a/Application/src/commons/common/gui/FileChooser.cpp b/Application/src/commons/common/gui/FileChooser.cpp
index 8a769c1..1ba0181 100644
--- a/Application/src/commons/common/gui/FileChooser.cpp
+++ b/Application/src/commons/common/gui/FileChooser.cpp
@@ -119,7 +119,7 @@ FileChooser::FileChooser(const file::Path& start, const std::string& extension,
     _columns->set_name("Columns");
     _rows->set_name("Rows");
     
-    if(_current_tab.content)
+    if(_current_tab.content && !_selected_file.empty())
         _columns->set_children({_rows, _current_tab.content});
     else
         _columns->set_children({_rows});
@@ -164,10 +164,6 @@ FileChooser::FileChooser(const file::Path& start, const std::string& extension,
     });
 }
 
-void FileChooser::set_on_update(std::function<void (DrawStructure &)> && fn) {
-    _on_update = std::move(fn);
-}
-
 void FileChooser::set_tabs(const std::vector<Settings>& tabs) {
     _tabs.clear();
     tabs_elements.clear();
@@ -215,17 +211,24 @@ void FileChooser::set_tabs(const std::vector<Settings>& tabs) {
 
 void FileChooser::set_tab(std::string tab) {
     if(tab != _current_tab.name) {
-        
     } else
         return;
     
     if(tab.empty()) {
         _current_tab = _default_tab;
+        
+        if(_on_tab_change)
+            _on_tab_change(_current_tab.name);
+        file_selected(0, "");
+        
     } else if(!_tabs.count(tab)) {
         auto str = Meta::toStr(_tabs);
         Except("FileChooser %S does not contain tab '%S'.", &str, &tab);
     } else {
         _current_tab = _tabs.at(tab);
+        if(_on_tab_change)
+            _on_tab_change(_current_tab.name);
+        file_selected(0, "");
     }
     
     for(auto &ptr : tabs_elements) {
@@ -245,6 +248,7 @@ void FileChooser::set_tab(std::string tab) {
         _current_tab.content->set_name("Extra");
     }
     
+    update_size();
     _graph->set_dirty(&_base);
 }
 
@@ -321,32 +325,48 @@ void FileChooser::change_folder(const file::Path& p) {
 }
 
 void FileChooser::file_selected(size_t, file::Path p) {
-    if(p.str() == ".." || p.is_folder()) {
+    if(!p.empty() && (p.str() == ".." || p.is_folder())) {
         change_folder(p);
+        
     } else {
         _selected_file = p;
-        if(!_selected_text)
-            _selected_text = std::make_shared<StaticText>("Selected: "+_selected_file.str(), Vec2(), Vec2(700, 0), Font(0.6));
-        else
-            _selected_text->set_txt("Selected: "+_selected_file.str());
-        
-        if(_tabs_bar)
-            _overall->set_children({
-                _tabs_bar,
-                _columns,
-                _selected_text,
-                _button
-            });
-        else
-            _overall->set_children({
-                _columns,
-                _selected_text,
-                _button
-            });
+        if(p.empty()) {
+            _selected_file = file::Path();
+            _selected_text = nullptr;
+            if(_tabs_bar)
+                _overall->set_children({
+                    _tabs_bar,
+                    _columns
+                });
+            else
+                _overall->set_children({
+                    _columns
+                });
+            
+        } else {
+            if(!_selected_text)
+                _selected_text = std::make_shared<StaticText>("Selected: "+_selected_file.str(), Vec2(), Vec2(700, 0), Font(0.6));
+            else
+                _selected_text->set_txt("Selected: "+_selected_file.str());
+            
+            if(_tabs_bar)
+                _overall->set_children({
+                    _tabs_bar,
+                    _columns,
+                    _selected_text,
+                    _button
+                });
+            else
+                _overall->set_children({
+                    _columns,
+                    _selected_text,
+                    _button
+                });
+        }
         _overall->update_layout();
         //update_size();
         
-        if(_on_select_callback)
+        if(!_selected_file.empty() && _on_select_callback)
             _on_select_callback(_selected_file, _current_tab.extension);
         update_size();
     }
@@ -355,16 +375,16 @@ void FileChooser::file_selected(size_t, file::Path p) {
 }
 
 void FileChooser::update_size() {
-    float left_column_width = _graph->width() - 20 - (_current_tab.content && _current_tab.content->width() > 20 ? _current_tab.content->width() + 10 : 0) - 10;
-    if(_selected_text) {
+    float left_column_width = _graph->width() - 20 - (_current_tab.content && _current_tab.content->width() > 20 && !_selected_file.empty() ? _current_tab.content->width() + 10 : 0) - 10;
+    if(_selected_text && !_selected_file.empty()) {
         _selected_text->set_max_size(Size2(_graph->width() - 20));
     }
     
     if(_tabs_bar) _tabs_bar->auto_size(Margin{0,0});
     if(_tabs_bar) _tabs_bar->update_layout();
     
-    float left_column_height = _graph->height() - 70 - 10 - (!_selected_file.empty() ? _button->height() + 10 : 0) - (_tabs_bar ? _tabs_bar->height() + 10 : 0) - (_selected_text && !_selected_file.empty() ? _selected_text->height() : 0);
-    _list->set_bounds(Bounds(0, 0, left_column_width, left_column_height - 70));
+    float left_column_height = _graph->height() - 70 - 10 - (_selected_text && !_selected_file.empty() ? _button->height() + 65 : 0) - (_tabs_bar ? _tabs_bar->height() + 10 : 0) - 25;
+    _list->set_bounds(Bounds(0, 0, left_column_width, left_column_height));
     
     _textfield->set_bounds(Bounds(0, 0, left_column_width, 30));
     _button->set_bounds(Bounds(_list->pos() + Vec2(0, left_column_height), Size2(100, 30)));
@@ -372,7 +392,7 @@ void FileChooser::update_size() {
     if(_rows) _rows->auto_size(Margin{0,0});
     if(_rows) _rows->update_layout();
     
-    if(_current_tab.content)
+    if(_current_tab.content && !_selected_file.empty())
         _columns->set_children({_rows, _current_tab.content});
     else
         _columns->set_children({_rows});
diff --git a/Application/src/commons/common/gui/FileChooser.h b/Application/src/commons/common/gui/FileChooser.h
index f67bd30..4310e21 100644
--- a/Application/src/commons/common/gui/FileChooser.h
+++ b/Application/src/commons/common/gui/FileChooser.h
@@ -66,6 +66,7 @@ protected:
     std::function<void(DrawStructure&)> _on_update;
     std::function<bool(file::Path)> _validity;
     std::function<void(file::Path)> _on_open;
+    std::function<void(std::string)> _on_tab_change;
     std::queue<std::function<void()>> _execute;
     std::mutex _execute_mutex;
     std::map<std::string, Settings> _tabs;
@@ -82,8 +83,9 @@ public:
     void open();
     void execute(std::function<void()>&&);
     void update_size();
-    void set_on_update(std::function<void(DrawStructure&)>&&);
-    void set_on_open(std::function<void(file::Path)>&& fn) { _on_open = std::move(fn); }
+    void on_update(std::function<void(DrawStructure&)>&& fn) { _on_update = std::move(fn); }
+    void on_open(std::function<void(file::Path)>&& fn) { _on_open = std::move(fn); }
+    void on_tab_change(std::function<void(std::string)>&& fn) { _on_tab_change = std::move(fn); }
     void set_validity_check(std::function<bool(file::Path)>&& fn) { _validity = std::move(fn); }
     
 private:
diff --git a/Application/src/commons/common/gui/types/Checkbox.h b/Application/src/commons/common/gui/types/Checkbox.h
index 56a5c0a..d265a0d 100644
--- a/Application/src/commons/common/gui/types/Checkbox.h
+++ b/Application/src/commons/common/gui/types/Checkbox.h
@@ -21,7 +21,7 @@ protected:
 public:
     Checkbox(const Vec2& pos, const std::string& text = "", bool checked = false, const Font& font = Font(0.75));
     
-    void set_callback(const decltype(_callback)& callback) {
+    void on_change(const decltype(_callback)& callback) {
         _callback = callback;
     }
     
diff --git a/Application/src/commons/common/gui/types/Textfield.h b/Application/src/commons/common/gui/types/Textfield.h
index d169e72..94c99ec 100644
--- a/Application/src/commons/common/gui/types/Textfield.h
+++ b/Application/src/commons/common/gui/types/Textfield.h
@@ -50,6 +50,8 @@ namespace gui {
             
             _font = font;
             _text_display.set_font(font);
+            if(_placeholder)
+                _placeholder->set_font(font);
             
             set_content_changed(true);
         }
diff --git a/Application/src/commons/common/misc/metastring.h b/Application/src/commons/common/misc/metastring.h
index eb7db57..51d9052 100644
--- a/Application/src/commons/common/misc/metastring.h
+++ b/Application/src/commons/common/misc/metastring.h
@@ -71,6 +71,31 @@ namespace cmn {
             return ss.str();
         }
         
+        std::string to_html() const {
+            static constexpr std::array<cmn::string_view, 5> names{{"us", "ms", "s", "min", "h"}};
+            static constexpr std::array<double, 5> ratios{{1000, 1000, 60, 60, 24}};
+            
+            double scaled = timestamp, previous_scaled = 0;
+            size_t i = 0;
+            while(i < ratios.size()-1 && scaled >= ratios[i]) {
+                scaled /= ratios[i];
+                
+                previous_scaled = scaled - size_t(scaled);
+                previous_scaled *= ratios[i];
+                
+                i++;
+            }
+            
+            size_t sub_part = (size_t)previous_scaled;
+            
+            std::stringstream ss;
+            ss << "<nr>" << std::fixed << std::setprecision(0) << scaled << "</nr>";
+            if(i>0 && i > 2)
+                ss << ":<nr>" << std::setfill('0') << std::setw(2) << sub_part << "</nr>";
+            ss << std::string(names[i].begin(), names[i].end());
+            return ss.str();
+        }
+        
         static DurationUS fromStr(const std::string&) {
             U_EXCEPTION("Not implemented.");
             return DurationUS{0};
diff --git a/Application/src/tracker/VideoOpener.cpp b/Application/src/tracker/VideoOpener.cpp
index 5f732c4..8ee50e6 100644
--- a/Application/src/tracker/VideoOpener.cpp
+++ b/Application/src/tracker/VideoOpener.cpp
@@ -15,12 +15,33 @@ namespace gui {
 GlobalSettings::docs_map_t temp_docs;
 sprite::Map temp_settings;
 
+VideoOpener::LabeledCheckbox::LabeledCheckbox(const std::string& name)
+    : LabeledField(name),
+      _checkbox(std::make_shared<gui::Checkbox>(Vec2(), name)),
+      _ref(gui::temp_settings[name])
+{
+    _checkbox->set_checked(_ref.value<bool>());
+    _checkbox->set_font(Font(0.6));
+
+    _checkbox->on_change([this](){
+        try {
+            _ref.get() = _checkbox->checked();
+
+        } catch(...) {}
+    });
+}
+
+void VideoOpener::LabeledCheckbox::update() {
+    _checkbox->set_checked(_ref.value<bool>());
+}
+
 VideoOpener::LabeledTextField::LabeledTextField(const std::string& name)
     : LabeledField(name),
-      _text_field(std::make_shared<gui::Textfield>("", Bounds(0, 0, 300, 33))),
+      _text_field(std::make_shared<gui::Textfield>("", Bounds(0, 0, 300, 28))),
       _ref(gui::temp_settings[name])
 {
     _text_field->set_placeholder(name);
+    _text_field->set_font(Font(0.6));
 
     _text_field->set_text(_ref.get().valueString());
     _text_field->on_text_changed([this](){
@@ -37,9 +58,10 @@ void VideoOpener::LabeledTextField::update() {
 
 VideoOpener::LabeledDropDown::LabeledDropDown(const std::string& name)
     : LabeledField(name),
-      _dropdown(std::make_shared<gui::Dropdown>(Bounds(0, 0, 300, 33))),
+      _dropdown(std::make_shared<gui::Dropdown>(Bounds(0, 0, 300, 28))),
       _ref(gui::temp_settings[name])
 {
+    _dropdown->textfield()->set_font(Font(0.6));
     assert(_ref.get().is_enum());
     std::vector<Dropdown::TextItem> items;
     int index = 0;
@@ -79,6 +101,7 @@ VideoOpener::VideoOpener() {
     TEMP_SETTING(output_name) = file::Path("video");
     
     _horizontal_raw = std::make_shared<gui::HorizontalLayout>();
+    _horizontal_raw->set_clickable(true);
     _raw_settings = std::make_shared<gui::VerticalLayout>();
     _raw_info = std::make_shared<gui::VerticalLayout>();
     _raw_info->set_policy(gui::VerticalLayout::LEFT);
@@ -101,14 +124,16 @@ VideoOpener::VideoOpener() {
     _text_fields["average_samples"] = std::make_unique<LabeledTextField>("average_samples");
     _text_fields["averaging_method"] = std::make_unique<LabeledDropDown>("averaging_method");
     
-    std::vector<Layout::Ptr> objects{};
+    std::vector<Layout::Ptr> objects{
+        Layout::Ptr(std::make_shared<Text>("Settings", Vec2(), White, gui::Font(0.8, Style::Bold)))
+    };
     for(auto &[key, ptr] : _text_fields)
         ptr->add_to(objects);
     
     _raw_settings->set_children(objects);
     
-    _raw_description = std::make_shared<gui::StaticText>("Info", Vec2(), Size2(500, -1));
-    _raw_info->set_children({_screenshot, _raw_description});
+    _raw_description = std::make_shared<gui::StaticText>("Info", Vec2(), Size2(400, -1), Font(0.6));
+    _raw_info->set_children({Layout::Ptr(std::make_shared<Text>("Preview", Vec2(), White, gui::Font(0.8, Style::Bold))), _screenshot, _raw_description});
     _horizontal_raw->set_children({_raw_settings, _raw_info});
     _horizontal_raw->set_policy(gui::HorizontalLayout::TOP);
     
@@ -200,14 +225,16 @@ VideoOpener::VideoOpener() {
             
         }
         
-    }, [this](auto& path, std::string tab){ select_file(path); });
+    }, [this](auto& path, std::string tab) {
+        select_file(path);
+    });
     
     _file_chooser->set_tabs({
         FileChooser::Settings{std::string("Pre-processed (PV)"), std::string("pv"), _horizontal},
         FileChooser::Settings{std::string("Convert (RAW)"), std::string("mp4;avi;mov;flv;m4v;webm"), _horizontal_raw}
     });
     
-    _file_chooser->set_on_update([this](auto&) mutable {
+    _file_chooser->on_update([this](auto&) mutable {
         std::lock_guard guard(_video_mutex);
         if(_buffer) {
             auto image = _buffer->next();
@@ -237,7 +264,11 @@ VideoOpener::VideoOpener() {
         return false;
     });
     
-    _file_chooser->set_on_open([this](auto){
+    _file_chooser->on_open([this](auto){
+        _buffer = nullptr;
+    });
+    
+    _file_chooser->on_tab_change([this](auto){
         _buffer = nullptr;
     });
     
@@ -403,9 +434,12 @@ std::unique_ptr<Image> VideoOpener::BufferedVideo::next() {
 
 void VideoOpener::select_file(const file::Path &p) {
     const double max_width = 500;
+    std::lock_guard guard(_file_chooser->graph()->lock());
     
-    if(!p.empty() && (!p.has_extension() || p.extension() != "pv")) {
+    if(_file_chooser->current_tab().extension != "pv") {
         try {
+            if(p.empty())
+                U_EXCEPTION("No file selected.");
             Debug("Opening '%S'", &p.str());
             
             std::lock_guard guard(_video_mutex);
@@ -443,6 +477,17 @@ void VideoOpener::select_file(const file::Path &p) {
                 Except("Converting number: '%s'", e.what());
             }
             
+            {
+                std::string info_text = "<h3>Info</h3>\n";
+                info_text += "<key>resolution</key>: <ref><nr>"+Meta::toStr(_buffer->_video->size().width)+"</nr>x<nr>"+Meta::toStr(_buffer->_video->size().height)+"</nr></ref>\n";
+                
+                DurationUS us{ uint64_t( _buffer->_video->length() / double(_buffer->_video->framerate()) * 1000.0 * 1000.0 ) };
+                auto len = us.to_html();
+                info_text += "<key>length</key>: <ref>"+len+"</ref>";
+                
+                _raw_description->set_txt(info_text);
+            }
+            
             auto ratio = max_width / _screenshot->size().max();
             Debug("%f (%f / %f)", ratio, max_width, _screenshot->size().max());
             _screenshot->set_scale(Vec2(ratio));
@@ -452,6 +497,7 @@ void VideoOpener::select_file(const file::Path &p) {
             _horizontal_raw->auto_size(Margin{0, 0});
             
         } catch(const std::exception& e) {
+            std::lock_guard guard(_video_mutex);
             Except("Cannot open file '%S' (%s)", &p.str(), e.what());
             
             cv::Mat img = cv::Mat::zeros(max_width, max_width, CV_8UC1);
@@ -503,6 +549,7 @@ void VideoOpener::select_file(const file::Path &p) {
     std::vector<Layout::Ptr> children {
         Layout::Ptr(std::make_shared<Text>("Settings", Vec2(), White, gui::Font(0.8, Style::Bold)))
     };
+    
     for(auto &name : _settings_to_show) {
         std::string start;
         if(tmp[name].is_type<std::string>())
@@ -511,7 +558,7 @@ void VideoOpener::select_file(const file::Path &p) {
             start = tmp[name].get().valueString();
         
         if(tmp[name].is_type<bool>()) {
-            children.push_back( Layout::Ptr(std::make_shared<Checkbox>(Vec2(), name, tmp[name].get().value<bool>(), gui::Font(0.7, Style::Bold))) );
+            children.push_back( Layout::Ptr(std::make_shared<Checkbox>(Vec2(), name, tmp[name].get().value<bool>(), gui::Font(0.6))) );
         } else if(name == "output_prefix") {
             std::vector<std::string> folders;
             for(auto &p : _selected.remove_filename().find_files()) {
@@ -526,12 +573,14 @@ void VideoOpener::select_file(const file::Path &p) {
                 }
             }
             
-            children.push_back( Layout::Ptr(std::make_shared<Text>(name, Vec2(), White, gui::Font(0.7, Style::Bold))) );
-            children.push_back( Layout::Ptr(std::make_shared<Dropdown>(Bounds(0, 0, 300, 30), folders)) );
+            children.push_back( Layout::Ptr(std::make_shared<Text>(name, Vec2(), White, gui::Font(0.6))) );
+            children.push_back( Layout::Ptr(std::make_shared<Dropdown>(Bounds(0, 0, 300, 28), folders)) );
+            ((Dropdown*)children.back().get())->textfield()->set_font(Font(0.6));
             
         } else {
-            children.push_back( Layout::Ptr(std::make_shared<Text>(name, Vec2(), White, gui::Font(0.7, Style::Bold))) );
-            children.push_back( Layout::Ptr(std::make_shared<Textfield>(start, Bounds(0, 0, 300, 30))));
+            children.push_back( Layout::Ptr(std::make_shared<Text>(name, Vec2(), White, gui::Font(0.6))) );
+            children.push_back( Layout::Ptr(std::make_shared<Textfield>(start, Bounds(0, 0, 300, 28))));
+            ((Textfield*)children.back().get())->set_font(Font(0.6));
         }
         
         if(name == "output_prefix") {
diff --git a/Application/src/tracker/VideoOpener.h b/Application/src/tracker/VideoOpener.h
index a6360d7..5e86854 100644
--- a/Application/src/tracker/VideoOpener.h
+++ b/Application/src/tracker/VideoOpener.h
@@ -83,7 +83,7 @@ public:
             : _text(std::make_shared<gui::Text>(name))
               //_joint(std::make_shared<gui::HorizontalLayout>(std::vector<Layout::Ptr>{_text, _text_field}))
         {
-            _text->set_font(Font(0.75, Style::Bold));
+            _text->set_font(Font(0.6));
             _text->set_color(White);
         }
         
@@ -114,6 +114,16 @@ public:
         }
         void update() override;
     };
+    struct LabeledCheckbox : public LabeledField {
+        gui::derived_ptr<gui::Checkbox> _checkbox;
+        sprite::Reference _ref;
+        LabeledCheckbox(const std::string& name = "");
+        void add_to(std::vector<Layout::Ptr>& v) override {
+            LabeledField::add_to(v);
+            v.push_back(_checkbox);
+        }
+        void update() override;
+    };
     std::map<std::string, std::unique_ptr<LabeledField>> _text_fields;
     
     gui::Checkbox *_load_results_checkbox = nullptr;
diff --git a/Application/src/tracker/gui/gui.cpp b/Application/src/tracker/gui/gui.cpp
index 9f95515..8dcbfcf 100644
--- a/Application/src/tracker/gui/gui.cpp
+++ b/Application/src/tracker/gui/gui.cpp
@@ -1220,11 +1220,12 @@ void GUI::reanalyse_from(long_t frame, bool in_thread) {
             gui->analysis()->set_paused(true).get();
         
         {
+            Tracker::instance()->wait();
+            
             std::lock_guard<std::recursive_mutex> gguard(gui->gui().lock());
             Tracker::LockGuard guard("reanalyse_from");
             
             if(frame <= Tracker::end_frame()) {
-                Tracker::instance()->wait();
                 Tracker::instance()->_remove_frames(frame);
                 gui->removed_frames(frame);
                 
diff --git a/Application/src/tracker/main.cpp b/Application/src/tracker/main.cpp
index f9ca584..505e975 100644
--- a/Application/src/tracker/main.cpp
+++ b/Application/src/tracker/main.cpp
@@ -1186,6 +1186,8 @@ int main(int argc, char** argv)
                         Debug("Paused.");
                     }
                 });
+                
+                Debug("Added.");
             }
         }
     });
diff --git a/Application/src/tracker/tracking/Tracker.cpp b/Application/src/tracker/tracking/Tracker.cpp
index 0c8c035..815cbdc 100644
--- a/Application/src/tracker/tracking/Tracker.cpp
+++ b/Application/src/tracker/tracking/Tracker.cpp
@@ -350,9 +350,10 @@ void Tracker::analysis_state(AnalysisState pause) {
         
         if (!callback_registered) {
             auto variable_changed = [](auto&map, auto&key, auto&value){ if(contains(Settings::names(), key)) {
-                Tracker::LockGuard guard("changed_settings");
-                Settings :: variable_changed(map, key, value);
-            }};
+                    Tracker::LockGuard guard("changed_settings");
+                    Settings :: variable_changed(map, key, value);
+                }
+            };
             cmn::GlobalSettings::map().register_callback((void*)"Settings", variable_changed);
             for(auto &n : Settings :: names())
                 variable_changed(cmn::GlobalSettings::map(), n, cmn::GlobalSettings::get(n).get());
@@ -3458,7 +3459,6 @@ void Tracker::update_iterator_maps(long_t frame, const Tracker::set_of_individua
     }
 
     void Tracker::wait() {
-        LockGuard guard("Tracker::wait()");
         recognition_pool.wait();
     }
 
-- 
GitLab