From 4426f70de70daf55d0322222f9fea8ae435daae1 Mon Sep 17 00:00:00 2001 From: Bruno Herbelin Date: Sat, 1 Jan 2022 23:59:30 +0100 Subject: [PATCH] Code editor for Custom pattern gstreamer Bugfix in Streamsource and UI --- BaseToolkit.cpp | 45 ----------------- BaseToolkit.h | 4 -- ImGuiToolkit.cpp | 101 +++++++++++++++++++++++---------------- ImGuiToolkit.h | 2 +- ImGuiVisitor.cpp | 15 ++++-- Stream.cpp | 2 +- StreamSource.cpp | 4 +- UserInterfaceManager.cpp | 52 +++++++++++++------- 8 files changed, 109 insertions(+), 116 deletions(-) diff --git a/BaseToolkit.cpp b/BaseToolkit.cpp index 70d2bb0..abb9ca4 100644 --- a/BaseToolkit.cpp +++ b/BaseToolkit.cpp @@ -111,51 +111,6 @@ std::string BaseToolkit::unspace(const std::string &input) return output; } -std::string BaseToolkit::unwrapped(const std::string &input) -{ - std::string output = input; - std::replace( output.begin(), output.end(), '\n', ' '); - return output; -} - -std::string BaseToolkit::wrapped(const std::string &input, size_t per_line) -{ - std::string text(input); - size_t line_begin = 0; - - while (line_begin < text.size()) - { - const size_t ideal_end = line_begin + per_line ; - size_t line_end = ideal_end <= text.size() ? ideal_end : text.size()-1; - - if (line_end == text.size() - 1) - ++line_end; - else if (std::isspace(text[line_end])) - { - text[line_end] = '\n'; - ++line_end; - } - else // backtrack - { - size_t end = line_end; - while ( end > line_begin && !std::isspace(text[end])) - --end; - - if (end != line_begin) - { - line_end = end; - text[line_end++] = '\n'; - } - else - text.insert(line_end++, 1, '\n'); - } - - line_begin = line_end; - } - - return text; -} - std::string BaseToolkit::byte_to_string(long b) { double numbytes = static_cast(b); diff --git a/BaseToolkit.h b/BaseToolkit.h index 06629ad..3cdf43c 100644 --- a/BaseToolkit.h +++ b/BaseToolkit.h @@ -19,10 +19,6 @@ std::string transliterate(const std::string &input); // replaces spaces by underscores in a string std::string unspace(const std::string &input); -// get a wrapped version of the input -std::string wrapped(const std::string &input, size_t per_line); -std::string unwrapped(const std::string &input); - // get a string to display memory size with unit KB, MB, GB, TB std::string byte_to_string(long b); diff --git a/ImGuiToolkit.cpp b/ImGuiToolkit.cpp index 36fdaab..7afae02 100644 --- a/ImGuiToolkit.cpp +++ b/ImGuiToolkit.cpp @@ -1677,41 +1677,6 @@ void word_wrap(std::string *str, unsigned per_line) } -void wrap(std::string *text, size_t per_line) -{ - size_t line_begin = 0; - - while (line_begin < text->size()) - { - const size_t ideal_end = line_begin + per_line ; - size_t line_end = ideal_end <= text->size() ? ideal_end : text->size()-1; - - if (line_end == text->size() - 1) - ++line_end; - else if (std::isspace(text->at(line_end))) - { - text->replace(line_end, 1, 1, '\n'); - ++line_end; - } - else // backtrack - { - size_t end = line_end; - while ( end > line_begin && !std::isspace(text->at(end))) - --end; - - if (end != line_begin) - { - line_end = end; - text->replace(line_end++, 1, 1, '\n'); - } - else - text->insert(line_end++, 1, '\n'); - } - - line_begin = line_end; - } -} - static int InputTextCallback(ImGuiInputTextCallbackData* data) { @@ -1746,8 +1711,8 @@ void ImGuiToolkit::TextMultiline(const char* label, const std::string &str, floa { size_t numlines = std::count(str.begin(), str.end(), '\n') + 1; - ImGuiContext& g = *GImGui; - ImVec2 size(width, numlines * (g.FontSize + g.Style.ItemSpacing.y) + g.Style.FramePadding.y * 2.0f); + const ImGuiContext& g = *GImGui; + ImVec2 size(width, numlines * g.FontSize + 2 * (g.Style.ItemSpacing.y + g.Style.FramePadding.y) ); ImGui::PushStyleColor(ImGuiCol_FrameBg, g.Style.Colors[ImGuiCol_FrameBgHovered]); ImGui::InputTextMultiline(label, (char*)str.c_str(), str.capacity() + 1, size, ImGuiInputTextFlags_ReadOnly); @@ -1755,7 +1720,48 @@ void ImGuiToolkit::TextMultiline(const char* label, const std::string &str, floa } -bool ImGuiToolkit::InputCodeMultiline(const char* label, std::string* str, const ImVec2& size) +std::string unwrapp(const std::string &input) +{ + std::string output = input; + output.erase( std::remove( output.begin(), output.end(), '\n'), output.end()); + return output; +} + +std::string wrapp(const std::string &input, size_t per_line) +{ + std::string text(input); + size_t line_begin = 0; + + while (line_begin < text.size()) + { + const size_t ideal_end = line_begin + per_line ; + size_t line_end = ideal_end <= text.size() ? ideal_end : text.size()-1; + + if (line_end == text.size() - 1) + ++line_end; + else if (std::isspace(text[line_end])) + text.insert(++line_end, 1, '\n'); + else // backtrack + { + size_t end = line_end; + while ( end > line_begin && !std::isspace(text[end])) + --end; + + if (end != line_begin) + { + line_end = end; + text.insert(++line_end, 1, '\n'); + } + else + text.insert(line_end++, 1, '\n'); + } + line_begin = line_end; + } + + return text; +} + +bool ImGuiToolkit::InputCodeMultiline(const char* label, std::string *str, const ImVec2& size, int *numline) { bool ret = false; char hiddenlabel[256]; @@ -1767,13 +1773,22 @@ bool ImGuiToolkit::InputCodeMultiline(const char* label, std::string* str, const // prepare input multiline ImGuiInputTextFlags flags = ImGuiInputTextFlags_CallbackResize | ImGuiInputTextFlags_CtrlEnterForNewLine; + // work on a string wrapped to the number of chars in a line + std::string edited = wrapp( *str, (size_t) floor(size.x / onechar.x) - 1); + // Input text into std::string with callback - ImGui::InputTextMultiline(hiddenlabel, (char*)str->c_str(), str->capacity() + 1, size, flags, InputTextCallback, str); + ImGui::InputTextMultiline(hiddenlabel, (char*)edited.c_str(), edited.capacity() + 1, size, flags, InputTextCallback, &edited); if (ImGui::IsItemDeactivatedAfterEdit() ){ - wrap(str, (size_t) ceil(size.x / onechar.x) - 1); + // unwrap after edit + *str = unwrapp(edited); + // return number of lines ret = true; } + // number of lines + if (numline) + *numline = std::count(edited.begin(), edited.end(), '\n') + 1; + ImGui::PopFont(); // show label @@ -1789,7 +1804,11 @@ void ImGuiToolkit::CodeMultiline(const char* label, const std::string &str, floa sprintf(hiddenlabel, "##%s", label); ImGuiToolkit::PushFont(FONT_MONO); - ImGuiToolkit::TextMultiline(hiddenlabel, str, width); + static ImVec2 onechar = ImGui::CalcTextSize("C"); + + std::string displayed_text = wrapp( str, (size_t) floor(width / onechar.x) - 1); + + ImGuiToolkit::TextMultiline(hiddenlabel, displayed_text, width); ImGui::PopFont(); ImGui::SameLine(); diff --git a/ImGuiToolkit.h b/ImGuiToolkit.h index 695186a..f109e07 100644 --- a/ImGuiToolkit.h +++ b/ImGuiToolkit.h @@ -71,7 +71,7 @@ namespace ImGuiToolkit bool InputTextMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0)); void TextMultiline(const char* label, const std::string &str, float width); - bool InputCodeMultiline(const char* label, std::string* str, const ImVec2& size = ImVec2(0, 0)); + bool InputCodeMultiline(const char* label, std::string *str, const ImVec2& size = ImVec2(0, 0), int *numline = NULL); void CodeMultiline(const char* label, const std::string &str, float width); // accent color of UI diff --git a/ImGuiVisitor.cpp b/ImGuiVisitor.cpp index 907608d..6d8eaa1 100644 --- a/ImGuiVisitor.cpp +++ b/ImGuiVisitor.cpp @@ -898,11 +898,16 @@ void ImGuiVisitor::visit (GenericStreamSource& s) ImGui::SetCursorPos(pos); } - // display pipeline text - std::string pipelinetxt = BaseToolkit::wrapped( s.description(), 20); - ImGuiToolkit::CodeMultiline("Pipeline", pipelinetxt, w); - - // // TODO allow editing pipeline + // Prepare display pipeline text + static int numlines = 0; + const ImGuiContext& g = *GImGui; + ImVec2 fieldsize(w, MAX(3, numlines) * g.FontSize + g.Style.ItemSpacing.y + g.Style.FramePadding.y); + // Editor + std::string _description = s.description(); + if ( ImGuiToolkit::InputCodeMultiline("Pipeline", &_description, fieldsize, &numlines) ) { + s.setDescription(_description); + Action::manager().store( s.name() + ": Change pipeline"); + } } diff --git a/Stream.cpp b/Stream.cpp index e828873..53f82dd 100644 --- a/Stream.cpp +++ b/Stream.cpp @@ -158,7 +158,7 @@ StreamInfo StreamDiscoverer(const std::string &description, guint w, guint h) std::mutex mtx; std::unique_lock lck(mtx); if ( info.discovered.wait_for(lck,std::chrono::seconds(TIMEOUT)) == std::cv_status::timeout) - info.message = "Time out."; + info.message = "Time out"; // stop and delete pipeline GstStateChangeReturn ret = gst_element_set_state (_pipeline, GST_STATE_NULL); diff --git a/StreamSource.cpp b/StreamSource.cpp index a86537b..f7c6791 100644 --- a/StreamSource.cpp +++ b/StreamSource.cpp @@ -45,7 +45,7 @@ GenericStreamSource::GenericStreamSource(uint64_t id) : StreamSource(id) void GenericStreamSource::setDescription(const std::string &desc) { - gst_description_ = BaseToolkit::unwrapped(desc); + gst_description_ = desc; gst_elements_ = BaseToolkit::splitted(desc, '!'); Log::Notify("Creating Source with Stream description '%s'", gst_description_.c_str()); @@ -82,6 +82,8 @@ glm::ivec2 GenericStreamSource::icon() const std::string GenericStreamSource::info() const { + if (gst_elements_.empty()) + return "Gstreamer custom pipeline without source"; std::string src_element = gst_elements_.front(); src_element = src_element.substr(0, src_element.find(" ")); return std::string("Gstreamer custom pipeline with source '")+src_element+"'"; diff --git a/UserInterfaceManager.cpp b/UserInterfaceManager.cpp index 996aa33..098ca85 100644 --- a/UserInterfaceManager.cpp +++ b/UserInterfaceManager.cpp @@ -4619,7 +4619,7 @@ void Navigator::RenderNewPannel() pattern_type = -1; } for (int p = 0; p < (int) Pattern::count(); ++p){ - if (Pattern::get(p).available && ImGui::Selectable( Pattern::get(p).label.c_str() )) { + if (Pattern::get(p).available && ImGui::Selectable( Pattern::get(p).label.c_str(), p == pattern_type )) { update_new_source = true; custom_pipeline = false; pattern_type = p; @@ -4633,20 +4633,44 @@ void Navigator::RenderNewPannel() ImGuiToolkit::HelpMarker("Create a source with graphics generated algorithmically."); if (custom_pipeline) { - static std::string description = "videotestsrc pattern=smpte"; + static std::vector< std::pair< std::string, std::string> > _examples = { {"Videotest", "videotestsrc horizontal-speed=1 " }, + {"Checker", "videotestsrc pattern=checkers-8 ! video/x-raw, width=64, height=64 "}, + {"Color", "videotestsrc pattern=gradient foreground-color= 0xff55f54f background-color= 0x000000 "}, + {"Text", "videotestsrc pattern=black ! textoverlay text=\"vimix\" halignment=center valignment=center font-desc=\"Sans,72\" "}, + {"GStreamer Webcam", "udpsrc port=5000 buffer-size=200000 ! h264parse ! avdec_h264 "}, + {"SRT listener", "srtsrc uri=\"srt://:5000?mode=listener\" ! decodebin "} + }; + static std::string _description = _examples[0].second; static ImVec2 fieldsize(ImGui::GetContentRegionAvail().x IMGUI_RIGHT_ALIGN, 100); + static int numlines = 0; + const ImGuiContext& g = *GImGui; + fieldsize.y = MAX(3, numlines) * g.FontSize + g.Style.ItemSpacing.y + g.Style.FramePadding.y; - size_t numlines = std::count(description.begin(), description.end(), '\n') + 1; - fieldsize.y = MAX( 100, numlines * ImGui::GetTextLineHeightWithSpacing() ); - - if ( ImGuiToolkit::InputCodeMultiline("Pipeline", &description, fieldsize) ) { - + // Editor + if ( ImGuiToolkit::InputCodeMultiline("Pipeline", &_description, fieldsize, &numlines) ) update_new_source = true; - } - if (update_new_source) { - new_source_preview_.setSource( Mixer::manager().createSourceStream(description), description.substr(0, description.find(" "))); + // Local menu for list of examples + ImVec2 pos_bot = ImGui::GetCursorPos(); + ImGui::SetCursorPos( pos_bot + ImVec2(fieldsize.x + IMGUI_SAME_LINE, -ImGui::GetFrameHeightWithSpacing())); + if ( ImGuiToolkit::ButtonIcon(16,10) ) + ImGui::OpenPopup( "MenuGstExamples" ); + ImGui::SetCursorPos(pos_bot); + if ( ImGui::BeginPopup( "MenuGstExamples" ) ) { + ImGui::TextDisabled("Gstreamer examples"); + ImGui::Separator(); + for (auto it = _examples.begin(); it != _examples.end(); ++it) { + if (ImGui::Selectable( it->first.c_str() ) ) { + _description = it->second; + update_new_source = true; + } + } + ImGui::EndPopup(); } + // take action + if (update_new_source) + new_source_preview_.setSource( Mixer::manager().createSourceStream(_description), _description.substr(0, _description.find(" "))); + } // if pattern selected else { @@ -5868,14 +5892,6 @@ void ShowSandbox(bool* p_open) ImGui::Separator(); - static std::string description = "A long string can always be wrapped into many pieces"; - - - if ( ImGuiToolkit::InputCodeMultiline("Wrapped", &description, ImVec2(ImGui::GetContentRegionAvail().x, 200))) { - - } - - ImGui::Text(description.c_str()); ImGui::End(); }