diff --git a/Recorder.cpp b/Recorder.cpp index 55ac283..9234fc9 100644 --- a/Recorder.cpp +++ b/Recorder.cpp @@ -462,6 +462,8 @@ void VideoRecorder::terminate() Log::Info("Video Recording : try a lower resolution / a lower framerate / a larger buffer size / a faster codec."); } + // remember and inform + Settings::application.recentRecordings.push(filename_); Log::Notify("Video Recording %s is ready.", filename_.c_str()); } diff --git a/Recorder.h b/Recorder.h index e586f2c..4f1b99e 100644 --- a/Recorder.h +++ b/Recorder.h @@ -12,11 +12,12 @@ class PNGRecorder : public FrameGrabber { - std::string filename_; + std::string filename_; public: PNGRecorder(); + std::string filename() const { return filename_; } protected: @@ -28,7 +29,7 @@ protected: class VideoRecorder : public FrameGrabber { - std::string filename_; + std::string filename_; std::string init(GstCaps *caps) override; void terminate() override; @@ -58,7 +59,7 @@ public: VideoRecorder(); std::string info() const override; - + std::string filename() const { return filename_; } }; diff --git a/Settings.cpp b/Settings.cpp index df125b4..2d4ee88 100644 --- a/Settings.cpp +++ b/Settings.cpp @@ -238,6 +238,29 @@ void Settings::Save(uint64_t runtime) } recent->InsertEndChild(recentmedia); + // recent import folders + XMLElement *recentimportfolder = xmlDoc.NewElement( "ImportFolder" ); + recentmedia->SetAttribute("path", application.recentImportFolders.path.c_str()); + for(auto it = application.recentImportFolders.filenames.cbegin(); + it != application.recentImportFolders.filenames.cend(); ++it) { + XMLElement *pathNode = xmlDoc.NewElement("path"); + XMLText *text = xmlDoc.NewText( (*it).c_str() ); + pathNode->InsertEndChild( text ); + recentimportfolder->InsertFirstChild(pathNode); + } + recent->InsertEndChild(recentimportfolder); + + // recent recordings + XMLElement *recentrecord = xmlDoc.NewElement( "Record" ); + for(auto it = application.recentRecordings.filenames.cbegin(); + it != application.recentRecordings.filenames.cend(); ++it) { + XMLElement *pathNode = xmlDoc.NewElement("path"); + XMLText *text = xmlDoc.NewText( (*it).c_str() ); + pathNode->InsertEndChild( text ); + recentrecord->InsertFirstChild(pathNode); + } + recent->InsertEndChild(recentrecord); + // recent dialog path XMLElement *recentdialogpath = xmlDoc.NewElement( "Dialog" ); for(auto it = application.dialogRecentFolder.cbegin(); @@ -517,6 +540,37 @@ void Settings::Load() application.recentImport.push( std::string (p) ); } } + + // recent import folders + XMLElement * pImportFolder = pElement->FirstChildElement("ImportFolder"); + if (pImportFolder) + { + const char *path_ = pImportFolder->Attribute("path"); + if (path_) + application.recentImportFolders.path = std::string(path_); + application.recentImportFolders.filenames.clear(); + XMLElement* path = pImportFolder->FirstChildElement("path"); + for( ; path ; path = path->NextSiblingElement()) + { + const char *p = path->GetText(); + if (p) + application.recentImportFolders.push( std::string (p) ); + } + } + // recent recordings + XMLElement * pRecord = pElement->FirstChildElement("Record"); + if (pRecord) + { + application.recentRecordings.filenames.clear(); + XMLElement* path = pRecord->FirstChildElement("path"); + for( ; path ; path = path->NextSiblingElement()) + { + const char *p = path->GetText(); + if (p) + application.recentRecordings.push( std::string (p) ); + } + } + // recent dialog path XMLElement * pDialog = pElement->FirstChildElement("Dialog"); if (pDialog) @@ -582,6 +636,37 @@ void Settings::History::validate() } } +// TODO unified history function saving +//void load_history(Settings::History &h, XMLElement *root) +//{ +// XMLElement * pElement = root->FirstChildElement("Import"); +// if (pElement) +// { +// h.filenames.clear(); +// XMLElement* path = pElement->FirstChildElement("path"); +// for( ; path ; path = path->NextSiblingElement()) +// { +// const char *p = path->GetText(); +// if (p) +// h.push( std::string (p) ); +// } +// } +//} + +//XMLElement *save_history(Settings::History &h, XMLDocument &xmlDoc) +//{ +// XMLElement *pElement = xmlDoc.NewElement( "Import" ); +// for(auto it = h.filenames.cbegin(); +// it != h.filenames.cend(); ++it) { +// XMLElement *fileNode = xmlDoc.NewElement("path"); +// XMLText *text = xmlDoc.NewText( (*it).c_str() ); +// fileNode->InsertEndChild( text ); +// pElement->InsertFirstChild(fileNode); +// } +// return pElement; +//} + + void Settings::Lock() { diff --git a/Settings.h b/Settings.h index 4689ce5..1e123eb 100644 --- a/Settings.h +++ b/Settings.h @@ -243,6 +243,8 @@ struct Application History recentSessions; History recentFolders; History recentImport; + History recentImportFolders; + History recentRecordings; std::map< std::string, std::string > dialogRecentFolder; // Metronome & stopwatch diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index 46604f6..d1d4fb1 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -111,8 +111,8 @@ std::string readable_date_time_string(std::string date){ // globals const std::chrono::milliseconds timeout = std::chrono::milliseconds(4); -std::vector< std::future > _video_recorders; -FrameGrabber *delayTrigger(FrameGrabber *g, std::chrono::milliseconds delay) { +std::vector< std::future > _video_recorders; +VideoRecorder *delayTrigger(VideoRecorder *g, std::chrono::milliseconds delay) { std::this_thread::sleep_for (delay); return g; } @@ -324,17 +324,20 @@ void UserInterface::handleKeyboard() FrameGrabbing::manager().add(new PNGRecorder); } else { - // toggle recording + // toggle recording stop / start if (video_recorder_) { - // allow 'save & continue' for Ctrl+Alt+R if no timeout for recording + // 'save & continue' for Ctrl+Alt+R if no timeout for recording if (alt_modifier_active && Settings::application.record.timeout == RECORD_MAX_TIMEOUT) { VideoRecorder *rec = new VideoRecorder; FrameGrabbing::manager().chain(video_recorder_, rec); video_recorder_ = rec; } // normal case: Ctrl+R stop recording - else + else { + // prepare for next open new source panel to show the recording + navigator.setNewMedia(Navigator::MEDIA_RECORDING, video_recorder_->filename()); video_recorder_->stop(); + } } else { _video_recorders.emplace_back( std::async(std::launch::async, delayTrigger, new VideoRecorder, @@ -795,7 +798,7 @@ void UserInterface::Render() } } // verify the video recorder is valid (change to nullptr if invalid) - FrameGrabbing::manager().verify(&video_recorder_); + FrameGrabbing::manager().verify( (FrameGrabber**) &video_recorder_); if (video_recorder_ // if there is an ongoing recorder && Settings::application.record.timeout < RECORD_MAX_TIMEOUT // and if the timeout is valid && video_recorder_->duration() > Settings::application.record.timeout ) // and the timeout is reached @@ -1340,12 +1343,18 @@ void UserInterface::RenderPreview() ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_RECORD, 0.8f)); if ( ImGui::MenuItem( ICON_FA_SQUARE " Stop Record", CTRL_MOD "R") ) { video_recorder_->stop(); + // prepare for next open new source panel to show the recording + navigator.setNewMedia(Navigator::MEDIA_RECORDING, video_recorder_->filename()); } // offer the 'save & continue' recording for undefined duration if (Settings::application.record.timeout == RECORD_MAX_TIMEOUT) { if ( ImGui::MenuItem( ICON_FA_ARROW_ALT_CIRCLE_DOWN " Save & continue", CTRL_MOD "Alt+R") ) { + // prepare for next open new source panel to show the recording + navigator.setNewMedia(Navigator::MEDIA_RECORDING, video_recorder_->filename()); + // create a new recorder chainned to the current one VideoRecorder *rec = new VideoRecorder; FrameGrabbing::manager().chain(video_recorder_, rec); + // swap recorder video_recorder_ = rec; } } @@ -3700,6 +3709,9 @@ Navigator::Navigator() pannel_visible_ = false; view_pannel_visible = false; clearButtonSelection(); + + new_media_mode = MEDIA_RECENT; + new_media_mode_changed = true; } void Navigator::applyButtonSelection(int index) @@ -3724,7 +3736,8 @@ void Navigator::clearButtonSelection() // clear new source pannel new_source_preview_.setSource(); pattern_type = -1; - _selectedFiles.clear(); + sourceSequenceFiles.clear(); + setNewMedia(new_media_mode); } void Navigator::showPannelSource(int index) @@ -4037,6 +4050,43 @@ void Navigator::RenderSourcePannel(Source *s) } } + +void Navigator::setNewMedia(MediaCreateMode mode, std::string path) +{ + // switch new source to Media pannel + Settings::application.source.new_type = SOURCE_FILE; + + // change mode + new_media_mode = mode; + new_media_mode_changed = true; + // mode dependent actions + switch (new_media_mode) { + case MEDIA_RECENT: + // set filename + sourceMediaFileCurrent = path; + // set combo to 'recent files' + Settings::application.recentImportFolders.path = IMGUI_LABEL_RECENT_FILES; + break; + case MEDIA_RECORDING: + // set filename + sourceMediaFileCurrent = path; + // set combo to 'recent recordings' + Settings::application.recentImportFolders.path = IMGUI_LABEL_RECENT_RECORDS; + break; + default: + case MEDIA_FOLDER: + // reset filename + sourceMediaFileCurrent.clear(); + // set combo: a path was selected + if (!path.empty()) + Settings::application.recentImportFolders.path.assign(path); + break; + } + + // clear preview + new_source_preview_.setSource(); +} + void Navigator::RenderNewPannel() { // Next window is a side pannel @@ -4058,7 +4108,7 @@ void Navigator::RenderNewPannel() // // News Source selection pannel // - static const char* origin_names[5] = { ICON_FA_PHOTO_VIDEO " File", + static const char* origin_names[SOURCE_TYPES] = { ICON_FA_PHOTO_VIDEO " File", ICON_FA_SORT_NUMERIC_DOWN " Sequence", ICON_FA_PLUG " Connected", ICON_FA_COG " Generated", @@ -4071,66 +4121,157 @@ void Navigator::RenderNewPannel() ImGui::SetCursorPosY(2.f * width_); // File Source creation - if (Settings::application.source.new_type == 0) { + if (Settings::application.source.new_type == SOURCE_FILE) { static DialogToolkit::OpenMediaDialog fileimportdialog("Open Media"); + static DialogToolkit::OpenFolderDialog folderimportdialog("Select Folder"); // 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 File", ImVec2(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, 0)) ) fileimportdialog.open(); - // Indication ImGui::SameLine(); ImGuiToolkit::HelpMarker("Create a source from a file:\n" - ICON_FA_CARET_RIGHT " Video (*.mpg, *mov, *.avi, etc.)\n" - ICON_FA_CARET_RIGHT " Image (*.jpg, *.png, etc.)\n" - ICON_FA_CARET_RIGHT " Vector graphics (*.svg)\n" - ICON_FA_CARET_RIGHT " vimix session (*.mix)\n\n" - "(Equivalent to dropping the file in the workspace)"); + ICON_FA_CARET_RIGHT " Video (*.mpg, *mov, *.avi, etc.)\n" + ICON_FA_CARET_RIGHT " Image (*.jpg, *.png, etc.)\n" + ICON_FA_CARET_RIGHT " Vector graphics (*.svg)\n" + ICON_FA_CARET_RIGHT " vimix session (*.mix)\n" + "\nNB: Equivalent to dropping the file in the workspace"); // get media file if dialog finished if (fileimportdialog.closed()){ // get the filename from this file dialog - std::string open_filename = fileimportdialog.path(); - // create a source with this file - if (open_filename.empty()) { - new_source_preview_.setSource(); - 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); + std::string importpath = fileimportdialog.path(); + // switch to recent files + setNewMedia(MEDIA_RECENT, importpath); + // open file + if (!importpath.empty()) { + std::string label = BaseToolkit::transliterate( sourceMediaFileCurrent ); + new_source_preview_.setSource( Mixer::manager().createSourceFile(sourceMediaFileCurrent), label); } } - // combo of recent media filenames + // combo to offer lists ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); - if (ImGui::BeginCombo("##RecentImport", IMGUI_LABEL_RECENT_FILES)) + if (ImGui::BeginCombo("##SelectionNewMedia", BaseToolkit::trunc_string(Settings::application.recentImportFolders.path, 25).c_str() )) { - std::list recent = Settings::application.recentImport.filenames; - for (std::list::iterator path = recent.begin(); path != recent.end(); ++path ) - { - std::string recentpath(*path); - if ( SystemToolkit::file_exists(recentpath)) { - std::string label = BaseToolkit::transliterate( recentpath ); - label = BaseToolkit::trunc_string(label, 35); - if (ImGui::Selectable( label.c_str() )) { - new_source_preview_.setSource( Mixer::manager().createSourceFile(recentpath.c_str()), label); - } + // Mode MEDIA_RECENT : recent files + if (ImGui::Selectable( ICON_FA_LIST_OL IMGUI_LABEL_RECENT_FILES) ) { + setNewMedia(MEDIA_RECENT); + } + // Mode MEDIA_RECORDING : recent recordings + if (ImGui::Selectable( ICON_FA_LIST_UL IMGUI_LABEL_RECENT_RECORDS) ) { + setNewMedia(MEDIA_RECORDING); + } + // Mode MEDIA_FOLDER : known folders + for(auto foldername = Settings::application.recentImportFolders.filenames.begin(); + foldername != Settings::application.recentImportFolders.filenames.end(); foldername++) { + std::string f = std::string(ICON_FA_FOLDER) + " " + BaseToolkit::trunc_string( *foldername, 40); + if (ImGui::Selectable( f.c_str() )) { + setNewMedia(MEDIA_FOLDER, *foldername); } } + // Add a folder for MEDIA_FOLDER + if (ImGui::Selectable( ICON_FA_FOLDER_PLUS " Add Folder") ) { + folderimportdialog.open(); + } ImGui::EndCombo(); } + + // return from thread for folder openning + if (folderimportdialog.closed() && !folderimportdialog.path().empty()) { + Settings::application.recentImportFolders.push(folderimportdialog.path()); + setNewMedia(MEDIA_FOLDER, folderimportdialog.path()); + } + + // icons to clear lists or discarc folder + ImVec2 pos_top = ImGui::GetCursorPos(); + ImGui::SameLine(); + ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.7); + if ( new_media_mode == MEDIA_FOLDER ) { + if (ImGuiToolkit::IconButton( ICON_FA_FOLDER_MINUS, "Discard folder")) { + Settings::application.recentImportFolders.filenames.remove(Settings::application.recentImportFolders.path); + if (Settings::application.recentImportFolders.filenames.empty()) + // revert mode RECENT + setNewMedia(MEDIA_RECENT); + else + setNewMedia(MEDIA_FOLDER, Settings::application.recentImportFolders.filenames.front()); + } + } + else if ( new_media_mode == MEDIA_RECORDING ) { + if (ImGuiToolkit::IconButton( ICON_FA_BACKSPACE, "Clear list")) { + Settings::application.recentRecordings.filenames.clear(); + Settings::application.recentRecordings.front_is_valid = false; + setNewMedia(MEDIA_RECORDING); + } + } + else if ( new_media_mode == MEDIA_RECENT ) { + if (ImGuiToolkit::IconButton( ICON_FA_BACKSPACE, "Clear list")) { + Settings::application.recentImport.filenames.clear(); + Settings::application.recentImport.front_is_valid = false; + setNewMedia(MEDIA_RECENT); + } + } + ImGui::PopStyleVar(); + ImGui::SetCursorPos(pos_top); + + // change session list if changed + if (new_media_mode_changed || Settings::application.recentImport.changed || Settings::application.recentRecordings.changed) { + + // MODE RECENT + if ( new_media_mode == MEDIA_RECENT) { + // show list of recent imports + Settings::application.recentImport.validate(); + sourceMediaFiles = Settings::application.recentImport.filenames; + Settings::application.recentImport.changed = false; + } + // MODE RECORDINGS + else if ( new_media_mode == MEDIA_RECORDING) { + // show list of recent records + Settings::application.recentRecordings.validate(); + sourceMediaFiles = Settings::application.recentRecordings.filenames; + Settings::application.recentRecordings.changed = false; + } + // MODE LIST FOLDER + else if ( new_media_mode == MEDIA_FOLDER) { + // show list of media files in folder + sourceMediaFiles = SystemToolkit::list_directory( Settings::application.recentImportFolders.path, { MEDIA_FILES_PATTERN }); + } + // indicate the list changed (do not change at every frame) + new_media_mode_changed = false; + } + + // different labels for each mode + static const char *listboxname[3] = { "##NewSourceMediaRecent", "##NewSourceMediaRecording", "##NewSourceMediafolder"}; + // display the import-list and detect if one was selected + ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); + if (ImGui::ListBoxHeader(listboxname[new_media_mode], sourceMediaFiles.size(), CLAMP(sourceMediaFiles.size(), 4, 6)) ) { + // loop over list of files + for(auto it = sourceMediaFiles.begin(); it != sourceMediaFiles.end(); ++it) { + // build displayed file name + std::string filename = BaseToolkit::transliterate(*it); + std::string label = BaseToolkit::trunc_string(SystemToolkit::filename(filename), 25); + // add selectable item to ListBox; open if clickec + if (ImGui::Selectable( label.c_str(), sourceMediaFileCurrent.compare(*it) == 0 )) { + // set new source preview + new_source_preview_.setSource( Mixer::manager().createSourceFile(*it), filename); + // remember current list item + sourceMediaFileCurrent = *it; + } + } + ImGui::ListBoxFooter(); + } + } // Folder Source creator - else if (Settings::application.source.new_type == 1){ + else if (Settings::application.source.new_type == SOURCE_SEQUENCE){ bool update_new_source = false; static DialogToolkit::MultipleImagesDialog _selectImagesDialog("Select Images"); // clic button to load file if ( ImGui::Button( ICON_FA_IMAGES " Open images", ImVec2(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, 0)) ) { - _selectedFiles.clear(); + sourceSequenceFiles.clear(); _selectImagesDialog.open(); } @@ -4140,15 +4281,15 @@ void Navigator::RenderNewPannel() // return from thread for folder openning if (_selectImagesDialog.closed()) { - _selectedFiles = _selectImagesDialog.images(); - if (_selectedFiles.empty()) + sourceSequenceFiles = _selectImagesDialog.images(); + if (sourceSequenceFiles.empty()) Log::Notify("No file selected."); // ask to reload the preview update_new_source = true; } // multiple files selected - if (_selectedFiles.size() > 1) { + if (sourceSequenceFiles.size() > 1) { // set framerate static int _fps = 30; @@ -4164,27 +4305,27 @@ void Navigator::RenderNewPannel() } if (update_new_source) { - std::string label = BaseToolkit::transliterate( BaseToolkit::common_pattern(_selectedFiles) ); + std::string label = BaseToolkit::transliterate( BaseToolkit::common_pattern(sourceSequenceFiles) ); label = BaseToolkit::trunc_string(label, 35); - new_source_preview_.setSource( Mixer::manager().createSourceMultifile(_selectedFiles, _fps), label); + new_source_preview_.setSource( Mixer::manager().createSourceMultifile(sourceSequenceFiles, _fps), label); } } // single file selected - else if (_selectedFiles.size() > 0) { + else if (sourceSequenceFiles.size() > 0) { ImGui::Text("Single file selected"); if (update_new_source) { - std::string label = BaseToolkit::transliterate( _selectedFiles.front() ); + std::string label = BaseToolkit::transliterate( sourceSequenceFiles.front() ); label = BaseToolkit::trunc_string(label, 35); - new_source_preview_.setSource( Mixer::manager().createSourceFile(_selectedFiles.front()), label); + new_source_preview_.setSource( Mixer::manager().createSourceFile(sourceSequenceFiles.front()), label); } } } // Internal Source creator - else if (Settings::application.source.new_type == 4){ + else if (Settings::application.source.new_type == SOURCE_INTERNAL){ // fill new_source_preview with a new source ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); @@ -4213,7 +4354,7 @@ void Navigator::RenderNewPannel() ICON_FA_CARET_RIGHT " Clone other sources"); } // Generated Source creator - else if (Settings::application.source.new_type == 3){ + else if (Settings::application.source.new_type == SOURCE_GENERATED){ bool update_new_source = false; @@ -4255,7 +4396,7 @@ void Navigator::RenderNewPannel() } } // External source creator - else if (Settings::application.source.new_type == 2){ + else if (Settings::application.source.new_type == SOURCE_CONNECTED){ ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); if (ImGui::BeginCombo("##External", "Select device")) @@ -4290,7 +4431,7 @@ void Navigator::RenderNewPannel() // if a new source was added if (new_source_preview_.filled()) { // show preview - new_source_preview_.Render(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, Settings::application.source.new_type != 2); + new_source_preview_.Render(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN); // ask to import the source in the mixer ImGui::NewLine(); if (new_source_preview_.ready() && ImGui::Button( ICON_FA_CHECK " Create", ImVec2(pannel_width_ - padding_width_, 0)) ) { @@ -4340,13 +4481,13 @@ void Navigator::RenderMainPannelVimix() ImGui::SetNextItemWidth(IMGUI_RIGHT_ALIGN); if (ImGui::BeginCombo("##SelectionSession", BaseToolkit::trunc_string(Settings::application.recentFolders.path, 25).c_str() )) { - // Option 0 : recent files - if (ImGui::Selectable( ICON_FA_CLOCK IMGUI_LABEL_RECENT_FILES) ) { + // Mode 0 : recent files + if (ImGui::Selectable( ICON_FA_LIST_OL IMGUI_LABEL_RECENT_FILES) ) { Settings::application.recentFolders.path = IMGUI_LABEL_RECENT_FILES; selection_session_mode = 0; selection_session_mode_changed = true; } - // Options 1 : known folders + // Mode 1 : known folders for(auto foldername = Settings::application.recentFolders.filenames.begin(); foldername != Settings::application.recentFolders.filenames.end(); foldername++) { std::string f = std::string(ICON_FA_FOLDER) + " " + BaseToolkit::trunc_string( *foldername, 40); @@ -4358,7 +4499,7 @@ void Navigator::RenderMainPannelVimix() selection_session_mode_changed = true; } } - // Option 2 : add a folder + // Add a folder if (ImGui::Selectable( ICON_FA_FOLDER_PLUS " Add Folder") ) customFolder.open(); ImGui::EndCombo(); @@ -4390,7 +4531,7 @@ void Navigator::RenderMainPannelVimix() } } else { - if (ImGuiToolkit::IconButton( ICON_FA_BACKSPACE, "Clear history")) { + if (ImGuiToolkit::IconButton( ICON_FA_BACKSPACE, "Clear list")) { Settings::application.recentSessions.filenames.clear(); Settings::application.recentSessions.front_is_valid = false; // reload the list next time @@ -4690,7 +4831,7 @@ void Navigator::RenderMainPannelVimix() ImGui::SameLine(); ImGui::PushStyleVar(ImGuiStyleVar_Alpha, 0.7); - if (ImGuiToolkit::IconButton( ICON_FA_BACKSPACE, "Clear undo")) { + if (ImGuiToolkit::IconButton( ICON_FA_BACKSPACE, "Clear history")) { Action::manager().init(); } ImGui::PopStyleVar(); @@ -5226,7 +5367,7 @@ void SourcePreview::setSource(Source *s, const string &label) delete source_; source_ = s; - label_ = label; + label_ = BaseToolkit::trunc_string(label, 35); reset_ = true; } @@ -5237,7 +5378,7 @@ Source * SourcePreview::getSource() return s; } -void SourcePreview::Render(float width, bool controlbutton) +void SourcePreview::Render(float width) { if(source_) { // cancel if failed @@ -5270,15 +5411,21 @@ void SourcePreview::Render(float width, bool controlbutton) FrameBuffer *frame = source_->frame(); ImVec2 preview_size(width, width / frame->aspectRatio()); ImGui::Image((void*)(uintptr_t) frame->texture(), preview_size); - - if (controlbutton && source_->ready()) { - ImVec2 pos = ImGui::GetCursorPos(); - ImGui::SameLine(); - bool active = source_->active(); - if (ImGuiToolkit::IconToggle(12,7,1,8, &active)) - source_->setActive(active); - ImGui::SetCursorPos(pos); + // if the source is playable and once its ready + if (source_->playable() && source_->ready()) { + // activate the source on mouse over + bool activate = ImGui::IsItemHovered(); + if (source_->active() != activate) + source_->setActive(activate); + // show icon '>' to indicate if we can activate it + if (!activate) { + ImVec2 pos = ImGui::GetCursorPos(); + ImGui::SetCursorPos(pos + preview_size * ImVec2(0.5f, -0.6f)); + ImGuiToolkit::Icon(12,7); + ImGui::SetCursorPos(pos); + } } + // information text ImGuiToolkit::Icon(source_->icon().x, source_->icon().y); ImGui::SameLine(0, 10); ImGui::Text("%s", label_.c_str()); diff --git a/UserInterfaceManager.h b/UserInterfaceManager.h index ef03f8a..e76ff43 100644 --- a/UserInterfaceManager.h +++ b/UserInterfaceManager.h @@ -20,6 +20,7 @@ struct ImVec2; class MediaPlayer; class FrameBufferImage; class FrameGrabber; +class VideoRecorder; class SourcePreview { @@ -33,7 +34,7 @@ public: void setSource(Source *s = nullptr, const std::string &label = ""); Source *getSource(); - void Render(float width, bool controlbutton = false); + void Render(float width); bool ready() const; inline bool filled() const { return source_ != nullptr; } }; @@ -67,7 +68,6 @@ class Navigator bool view_pannel_visible; bool selected_button[NAV_COUNT]; int pattern_type; - std::list _selectedFiles; void clearButtonSelection(); void applyButtonSelection(int index); @@ -80,8 +80,6 @@ class Navigator void RenderNewPannel(); void RenderViewPannel(ImVec2 draw_pos, ImVec2 draw_size); - SourcePreview new_source_preview_; - public: Navigator(); @@ -92,7 +90,31 @@ public: void togglePannelNew(); void showConfig(); + typedef enum { + MEDIA_RECENT = 0, + MEDIA_RECORDING, + MEDIA_FOLDER + } MediaCreateMode; + void setNewMedia(MediaCreateMode mode, std::string path = std::string()); + void Render(); + +private: + // for new source panel + typedef enum { + SOURCE_FILE = 0, + SOURCE_SEQUENCE, + SOURCE_CONNECTED, + SOURCE_GENERATED, + SOURCE_INTERNAL, + SOURCE_TYPES + } NewSourceType; + SourcePreview new_source_preview_; + std::list sourceSequenceFiles; + std::list sourceMediaFiles; + std::string sourceMediaFileCurrent; + MediaCreateMode new_media_mode; + bool new_media_mode_changed; }; class ToolBox @@ -199,7 +221,7 @@ class UserInterface unsigned int screenshot_step; // frame grabbers - FrameGrabber *video_recorder_; + VideoRecorder *video_recorder_; #if defined(LINUX) FrameGrabber *webcam_emulator_; diff --git a/defines.h b/defines.h index 1fe2450..d73c8af 100644 --- a/defines.h +++ b/defines.h @@ -72,6 +72,7 @@ #define IMGUI_TITLE_PREVIEW ICON_FA_DESKTOP " Ouput" #define IMGUI_TITLE_DELETE ICON_FA_BROOM " Delete?" #define IMGUI_LABEL_RECENT_FILES " Recent files" +#define IMGUI_LABEL_RECENT_RECORDS " Recent recordings" #define IMGUI_RIGHT_ALIGN -3.5f * ImGui::GetTextLineHeightWithSpacing() #define IMGUI_TOP_ALIGN 10 #define IMGUI_COLOR_OVERLAY IM_COL32(5, 5, 5, 150)