diff --git a/rsc/images/icons.dds b/rsc/images/icons.dds index eeb0fbb..92c9236 100644 Binary files a/rsc/images/icons.dds and b/rsc/images/icons.dds differ diff --git a/src/CloneSource.cpp b/src/CloneSource.cpp index 99a409c..5e539e5 100644 --- a/src/CloneSource.cpp +++ b/src/CloneSource.cpp @@ -37,7 +37,7 @@ CloneSource::CloneSource(Source *origin, uint64_t id) : Source(id), origin_(origin), paused_(false), filter_(nullptr) { // initial name copies the origin name: diplucates are namanged in session - name_ = origin->name(); + name_ = origin_name_ = origin->name(); // set symbol symbol_ = new Symbol(Symbol::CLONE, glm::vec3(0.75f, 0.75f, 0.01f)); @@ -64,6 +64,7 @@ CloneSource::~CloneSource() void CloneSource::detach() { Log::Info("Source '%s' detached from '%s'.", name().c_str(), origin_->name().c_str() ); + origin_name_ = origin_->name(); origin_ = nullptr; } @@ -233,7 +234,7 @@ uint CloneSource::texture() const Source::Failure CloneSource::failed() const { - return (origin_ == nullptr || origin_->failed()) ? FAIL_FATAL : FAIL_NONE; + return (origin_ == nullptr || origin_->failed()) ? FAIL_RETRY : FAIL_NONE; } void CloneSource::accept(Visitor& v) @@ -251,3 +252,10 @@ std::string CloneSource::info() const { return "Clone"; } + +std::string CloneSource::origin_name() const +{ + if (origin_) + return origin_->name(); + return origin_name_; +} diff --git a/src/CloneSource.h b/src/CloneSource.h index 6c9431f..582ed57 100644 --- a/src/CloneSource.h +++ b/src/CloneSource.h @@ -32,6 +32,7 @@ public: // implementation of cloning mechanism void detach(); inline Source *origin() const { return origin_; } + std::string origin_name() const; // Filtering void setFilter(FrameBufferFilter::Type T); @@ -44,6 +45,7 @@ protected: void init() override; Source *origin_; + std::string origin_name_; // control bool paused_; diff --git a/src/ImGuiToolkit.cpp b/src/ImGuiToolkit.cpp index 01a0d4f..4ea0b64 100644 --- a/src/ImGuiToolkit.cpp +++ b/src/ImGuiToolkit.cpp @@ -171,7 +171,7 @@ bool ImGuiToolkit::ButtonSwitch(const char* label, bool* toggle, const char* too -void _drawIcon(ImVec2 screenpos, int i, int j, bool enabled = true) +void _drawIcon(ImVec2 screenpos, int i, int j, bool enabled = true, ImGuiWindow* window = ImGui::GetCurrentWindow() ) { // icons.dds is a 20 x 20 grid of icons if (textureicons == 0) @@ -184,7 +184,6 @@ void _drawIcon(ImVec2 screenpos, int i, int j, bool enabled = true) if (!enabled) tint_color = ImGui::GetStyle().Colors[ImGuiCol_TextDisabled]; - ImGuiWindow* window = ImGui::GetCurrentWindow(); ImRect bb(screenpos, screenpos + ImVec2(ImGui::GetTextLineHeightWithSpacing(), ImGui::GetTextLineHeightWithSpacing())); window->DrawList->AddImage((void*)(intptr_t)textureicons, bb.Min, bb.Max, uv0, uv1, ImGui::GetColorU32(tint_color) ); } @@ -565,14 +564,8 @@ bool ImGuiToolkit::ComboIcon (const char* label, int* current_item, *current_item = CLAMP( *current_item, 0, (int) items.size() - 1); - // make some space - char space_buf[] = " "; - const ImVec2 space_size = ImGui::CalcTextSize(" ", NULL); - const int space_num = static_cast( ceil(g.FontSize / space_size.x) ); - space_buf[space_num]='\0'; - char text_buf[256]; - ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "%s %s", space_buf, std::get<2>( items.at(*current_item) ).c_str()); + ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), " %s", std::get<2>( items.at(*current_item) ).c_str()); if ( ImGui::BeginCombo( label, text_buf, ImGuiComboFlags_None) ) { for (int p = 0; p < (int) items.size(); ++p){ ImGui::PushID((void*)(intptr_t)p); @@ -600,17 +593,12 @@ bool ImGuiToolkit::SelectableIcon(int i, int j, const char* label, bool selected { ImGuiContext& g = *GImGui; ImVec2 draw_pos = ImGui::GetCursorScreenPos() - g.Style.FramePadding * 0.5; - - // make some space - char space_buf[] = " "; const ImVec2 space_size = ImGui::CalcTextSize(" ", NULL); - const int space_num = static_cast( ceil(g.FontSize / space_size.x) ); - space_buf[space_num+1]='\0'; char text_buf[256]; - ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "%s%s", space_buf, label); + ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), " %s", label); - ImGui::PushID( i * 20 + j + ImGui::GetID("##SelectableIcon") ); + ImGui::PushID( i * 20 + j + ImGui::GetID(text_buf) ); // draw menu item bool ret = ImGui::Selectable(text_buf, selected, ImGuiSelectableFlags_None, size_arg); @@ -627,26 +615,39 @@ bool ImGuiToolkit::SelectableIcon(int i, int j, const char* label, bool selected return ret; } - bool ImGuiToolkit::MenuItemIcon (int i, int j, const char* label, const char* shortcut, bool selected, bool enabled) { ImVec2 draw_pos = ImGui::GetCursorScreenPos(); - - // make some space - char space_buf[] = " "; - const ImVec2 space_size = ImGui::CalcTextSize(" ", NULL); - const int space_num = static_cast( ceil(ImGui::GetTextLineHeightWithSpacing() / space_size.x) ); - space_buf[space_num]='\0'; + ImGuiContext& g = *GImGui; + draw_pos.y -= g.Style.ItemSpacing.y * 0.5f; char text_buf[256]; - ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), "%s %s", space_buf, label); + ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), " %s", label); // draw menu item bool ret = ImGui::MenuItem(text_buf, shortcut, selected, enabled); - // draw icon - ImGui::SetCursorScreenPos(draw_pos); - Icon(i, j, enabled); + // overlay of icon on top of first item + _drawIcon(draw_pos, i, j, enabled); + + return ret; +} + +bool ImGuiToolkit::BeginMenuIcon(int i, int j, const char *label, bool enabled) +{ + ImGuiWindow *win = ImGui::GetCurrentWindow(); + ImVec2 draw_pos = ImGui::GetCursorScreenPos(); + ImGuiContext &g = *GImGui; + draw_pos.y += g.Style.ItemSpacing.y * 0.5f; + + char text_buf[256]; + ImFormatString(text_buf, IM_ARRAYSIZE(text_buf), " %s", label); + + // draw menu item + bool ret = ImGui::BeginMenu(text_buf, enabled); + + // overlay of icon on top of first item + _drawIcon(draw_pos, i, j, enabled, win); return ret; } @@ -1763,7 +1764,7 @@ void ImGuiToolkit::SetAccentColor(accent_color color) colors[ImGuiCol_TextDisabled] = ImVec4(0.55f, 0.55f, 0.55f, 1.00f); colors[ImGuiCol_WindowBg] = ImVec4(0.13f, 0.13f, 0.13f, 0.94f); colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); - colors[ImGuiCol_PopupBg] = ImVec4(0.13f, 0.13f, 0.13f, 1.00f); + colors[ImGuiCol_PopupBg] = ImVec4(0.16f, 0.16f, 0.16f, 1.00f); colors[ImGuiCol_Border] = ImVec4(0.69f, 0.69f, 0.69f, 0.25f); colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_FrameBg] = ImVec4(0.39f, 0.39f, 0.39f, 0.55f); diff --git a/src/ImGuiToolkit.h b/src/ImGuiToolkit.h index 5930e92..e626e92 100644 --- a/src/ImGuiToolkit.h +++ b/src/ImGuiToolkit.h @@ -28,6 +28,7 @@ namespace ImGuiToolkit bool ButtonIcon (int i, int j, const char* tooltip = nullptr, bool expanded = false); bool ButtonIconToggle (int i, int j, bool* toggle, const char *tooltip = nullptr); bool ButtonIconMultistate (std::vector > icons, int* state, std::vector tooltips); + bool BeginMenuIcon(int i, int j, const char *label, bool enabled = true); bool MenuItemIcon (int i, int j, const char* label, const char* shortcut = nullptr, bool selected = false, bool enabled = true); bool SelectableIcon(int i, int j, const char* label, bool selected, const ImVec2& size_arg = ImVec2(0,0)); bool ComboIcon (const char* label, int* current_item, std::vector > items, std::vector tooltips = {}); diff --git a/src/ImageFilter.cpp b/src/ImageFilter.cpp index f1e7198..21a758c 100644 --- a/src/ImageFilter.cpp +++ b/src/ImageFilter.cpp @@ -272,7 +272,7 @@ void ImageFilteringShader::setCode(const std::string &code, std::promiseset_value("No change."); + ret->set_value("No change"); } } @@ -292,7 +292,7 @@ void ImageFilteringShader::copy(ImageFilteringShader const& S) /// //// //////////////////////////////////////// -ImageFilter::ImageFilter (): FrameBufferFilter(), buffers_({nullptr, nullptr}) +ImageFilter::ImageFilter (): FrameBufferFilter(), buffers_({nullptr, nullptr}), channel1_output_session(true) { // surface and shader for first pass shaders_.first = new ImageFilteringShader; @@ -381,6 +381,7 @@ void ImageFilter::draw (FrameBuffer *input) input_ = input; // create first-pass surface and shader, taking as texture the input framebuffer surfaces_.first->setTextureIndex( input_->texture() ); + shaders_.first->secondary_texture = input_->texture(); // (re)create framebuffer for result of first-pass if (buffers_.first != nullptr) delete buffers_.first; @@ -390,6 +391,7 @@ void ImageFilter::draw (FrameBuffer *input) input_->blit( buffers_.first ); // create second-pass surface and shader, taking as texture the first-pass framebuffer surfaces_.second->setTextureIndex( buffers_.first->texture() ); + shaders_.second->secondary_texture = input_->texture(); // (re)create framebuffer for result of second-pass if (buffers_.second != nullptr) delete buffers_.second; @@ -401,16 +403,18 @@ void ImageFilter::draw (FrameBuffer *input) if ( enabled() || forced ) { // FIRST PASS + if (channel1_output_session) + shaders_.first->secondary_texture = Mixer::manager().session()->frame()->texture(); // render input surface into frame buffer - shaders_.first->mask_texture = Mixer::manager().session()->frame()->texture(); buffers_.first->begin(); surfaces_.first->draw(glm::identity(), buffers_.first->projection()); buffers_.first->end(); // SECOND PASS if ( program_.isTwoPass() ) { + if (channel1_output_session) + shaders_.second->secondary_texture = Mixer::manager().session()->frame()->texture(); // render filtered surface from first pass into frame buffer - shaders_.second->mask_texture = Mixer::manager().session()->frame()->texture(); buffers_.second->begin(); surfaces_.second->draw(glm::identity(), buffers_.second->projection()); buffers_.second->end(); @@ -525,6 +529,7 @@ std::vector< FilteringProgram > ResampleFilter::programs_ = { ResampleFilter::ResampleFilter (): ImageFilter(), factor_(RESAMPLE_INVALID) { + channel1_output_session = false; } void ResampleFilter::setFactor(int factor) @@ -579,7 +584,7 @@ void ResampleFilter::draw (FrameBuffer *input) // create first-pass surface and shader, taking as texture the input framebuffer surfaces_.first->setTextureIndex( input_->texture() ); - shaders_.first->mask_texture = input_->texture(); + shaders_.first->secondary_texture = input_->texture(); // (re)create framebuffer for result of first-pass if (buffers_.first != nullptr) delete buffers_.first; @@ -604,7 +609,7 @@ void ResampleFilter::draw (FrameBuffer *input) // SECOND PASS for QUARTER resolution (divide by 2 after first pass divide by 2) // create second-pass surface and shader, taking as texture the first-pass framebuffer surfaces_.second->setTextureIndex( buffers_.first->texture() ); - shaders_.second->mask_texture = input_->texture(); + shaders_.second->secondary_texture = input_->texture(); // (re)create framebuffer for result of second-pass if (buffers_.second != nullptr) delete buffers_.second; @@ -662,6 +667,7 @@ std::vector< FilteringProgram > BlurFilter::programs_ = { BlurFilter::BlurFilter (): ImageFilter(), method_(BLUR_INVALID), mipmap_buffer_(nullptr) { mipmap_surface_ = new Surface; + channel1_output_session = false; } BlurFilter::~BlurFilter () @@ -733,7 +739,7 @@ void BlurFilter::draw (FrameBuffer *input) // create first-pass surface and shader, taking as texture the input framebuffer surfaces_.first->setTextureIndex( mipmap_buffer_->texture() ); - shaders_.first->mask_texture = input_->texture(); + shaders_.first->secondary_texture = input_->texture(); // (re)create framebuffer for result of first-pass if (buffers_.first != nullptr) delete buffers_.first; @@ -743,7 +749,7 @@ void BlurFilter::draw (FrameBuffer *input) // create second-pass surface and shader, taking as texture the first-pass framebuffer surfaces_.second->setTextureIndex( buffers_.first->texture() ); - shaders_.second->mask_texture = input_->texture(); + shaders_.second->secondary_texture = input_->texture(); // (re)create framebuffer for result of second-pass if (buffers_.second != nullptr) delete buffers_.second; @@ -803,6 +809,7 @@ std::vector< FilteringProgram > SharpenFilter::programs_ = { SharpenFilter::SharpenFilter (): ImageFilter(), method_(SHARPEN_INVALID) { + channel1_output_session = false; } void SharpenFilter::setMethod(int method) @@ -880,6 +887,7 @@ std::vector< FilteringProgram > SmoothFilter::programs_ = { SmoothFilter::SmoothFilter (): ImageFilter(), method_(SMOOTH_INVALID) { + channel1_output_session = false; } void SmoothFilter::setMethod(int method) @@ -950,6 +958,7 @@ std::vector< FilteringProgram > EdgeFilter::programs_ = { EdgeFilter::EdgeFilter (): ImageFilter(), method_(EDGE_INVALID) { + channel1_output_session = false; } void EdgeFilter::setMethod(int method) @@ -1020,6 +1029,7 @@ std::vector< FilteringProgram > AlphaFilter::programs_ = { AlphaFilter::AlphaFilter (): ImageFilter(), operation_(ALPHA_INVALID) { + channel1_output_session = false; } void AlphaFilter::setOperation(int op) diff --git a/src/ImageFilter.h b/src/ImageFilter.h index 2fe5161..afc5e8c 100644 --- a/src/ImageFilter.h +++ b/src/ImageFilter.h @@ -139,6 +139,7 @@ protected: std::pair< FrameBuffer *, FrameBuffer * > buffers_; std::pair< ImageFilteringShader *, ImageFilteringShader *> shaders_; void updateParameters(); + bool channel1_output_session; }; diff --git a/src/ImageShader.cpp b/src/ImageShader.cpp index ffee408..4b437a8 100644 --- a/src/ImageShader.cpp +++ b/src/ImageShader.cpp @@ -43,7 +43,7 @@ const char* MaskShader::mask_icons[4] = { ICON_FA_WINDOW_CLOSE, ICON_FA_EDIT, I const char* MaskShader::mask_names[4] = { "No mask", "Paint mask", "Shape mask", "Source mask" }; const char* MaskShader::mask_shapes[5] = { "Ellipse", "Oblong", "Rectangle", "Horizontal", "Vertical" }; -ImageShader::ImageShader(): Shader(), mask_texture(0), stipple(0.f) +ImageShader::ImageShader(): Shader(), secondary_texture(0), stipple(0.f) { // static program shader program_ = &imageShadingProgram; @@ -60,21 +60,21 @@ void ImageShader::use() program_->setUniform("iNodes", iNodes); // default mask - if (mask_texture == 0) - mask_texture = Resource::getTextureWhite(); + if (secondary_texture == 0) + secondary_texture = Resource::getTextureWhite(); // setup mask texture glActiveTexture(GL_TEXTURE1); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glBindTexture (GL_TEXTURE_2D, mask_texture); + glBindTexture (GL_TEXTURE_2D, secondary_texture); glActiveTexture(GL_TEXTURE0); } void ImageShader::reset() { Shader::reset(); - mask_texture = 0; + secondary_texture = 0; // no stippling stipple = 0.f; @@ -84,7 +84,7 @@ void ImageShader::reset() void ImageShader::copy(ImageShader const& S) { - mask_texture = S.mask_texture; + secondary_texture = S.secondary_texture; stipple = S.stipple; iNodes = S.iNodes; } diff --git a/src/ImageShader.h b/src/ImageShader.h index 096bd23..f2c497c 100644 --- a/src/ImageShader.h +++ b/src/ImageShader.h @@ -1,8 +1,6 @@ #ifndef IMAGESHADER_H #define IMAGESHADER_H -#include -#include #include #include "Shader.h" @@ -18,7 +16,7 @@ public: void accept(Visitor& v) override; void copy(ImageShader const& S); - uint mask_texture; + uint secondary_texture; // uniforms float stipple; diff --git a/src/Mixer.cpp b/src/Mixer.cpp index d29aae7..7c9097a 100644 --- a/src/Mixer.cpp +++ b/src/Mixer.cpp @@ -182,8 +182,14 @@ void Mixer::update() insertSource(candidate_sources_.front().first, candidate_sources_.size() > 1 ? View::INVALID : View::MIXING); // the second element of the pair is the source to be replaced, i.e. deleted if provided - if (candidate_sources_.front().second != nullptr) + if (candidate_sources_.front().second != nullptr) { + // keep previous name + std::string previous_name = candidate_sources_.front().second->name(); + // delete previous deleteSource(candidate_sources_.front().second); + // rename new source with previous name + candidate_sources_.front().first->setName(previous_name); + } candidate_sources_.pop_front(); } @@ -459,10 +465,10 @@ Source * Mixer::createSourceGroup() return s; } -Source * Mixer::createSourceClone(const std::string &namesource, bool copy_attributes) +CloneSource * Mixer::createSourceClone(const std::string &namesource, bool copy_attributes) { // ready to create a source - Source *s = nullptr; + CloneSource *s = nullptr; // origin to clone is either the given name or the current SourceList::iterator origin = session_->end(); @@ -589,9 +595,6 @@ void Mixer::replaceSource(Source *previous, Source *s) s->setImageProcessingEnabled( previous->imageProcessingEnabled() ); s->blendingShader()->blending = previous->blendingShader()->blending; - // rename s - renameSource(s, previous_name); - // add source 's' and remove source 'previous' candidate_sources_.push_back( std::make_pair(s, previous) ); } diff --git a/src/Mixer.h b/src/Mixer.h index c34f447..178d8aa 100644 --- a/src/Mixer.h +++ b/src/Mixer.h @@ -50,7 +50,7 @@ public: // creation of sources Source * createSourceFile (const std::string &path, bool disable_hw_decoding = false); Source * createSourceMultifile(const std::list &list_files, uint fps); - Source * createSourceClone (const std::string &namesource = "", bool copy_attributes = true); + class CloneSource *createSourceClone(const std::string &namesource = "", bool copy_attributes = true); Source * createSourceRender (); Source * createSourceStream (const std::string &gstreamerpipeline); Source * createSourcePattern(uint pattern, glm::ivec2 res); diff --git a/src/OutputPreviewWindow.cpp b/src/OutputPreviewWindow.cpp index 7c55187..4584f9c 100644 --- a/src/OutputPreviewWindow.cpp +++ b/src/OutputPreviewWindow.cpp @@ -385,7 +385,7 @@ void OutputPreviewWindow::Render() ImGui::EndMenu(); } - if (ImGui::BeginMenu(ICON_FA_WIFI " Stream")) + if (ImGuiToolkit::BeginMenuIcon(19, 11, "Stream")) { // Stream sharing menu ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(IMGUI_COLOR_STREAM, 0.9f)); diff --git a/src/SessionVisitor.cpp b/src/SessionVisitor.cpp index 816be06..c5b28a0 100644 --- a/src/SessionVisitor.cpp +++ b/src/SessionVisitor.cpp @@ -821,13 +821,12 @@ void SessionVisitor::visit (CloneSource& s) cloneNode->SetAttribute("type", "CloneSource"); // origin of clone - if (s.origin()) { - XMLElement *origin = xmlDoc_->NewElement( "origin" ); - cloneNode->InsertEndChild(origin); + XMLElement *origin = xmlDoc_->NewElement( "origin" ); + XMLText *text = xmlDoc_->NewText( s.origin_name().c_str() ); + if (s.origin()) origin->SetAttribute("id", s.origin()->id()); - XMLText *text = xmlDoc_->NewText( s.origin()->name().c_str() ); - origin->InsertEndChild( text ); - } + origin->InsertEndChild( text ); + cloneNode->InsertEndChild(origin); // Filter xmlCurrent_ = xmlDoc_->NewElement( "Filter" ); diff --git a/src/ShaderEditWindow.cpp b/src/ShaderEditWindow.cpp index 7f945bc..11b3961 100644 --- a/src/ShaderEditWindow.cpp +++ b/src/ShaderEditWindow.cpp @@ -18,6 +18,7 @@ TextEditor _editor; #include "SystemToolkit.h" #include "CloneSource.h" #include "DialogToolkit.h" +#include "UserInterfaceManager.h" #include "ShaderEditWindow.h" @@ -141,6 +142,8 @@ void ShaderEditWindow::Render() return; } + Source *cs = Mixer::manager().currentSource(); + // menu (no title bar) if (ImGui::BeginMenuBar()) { @@ -149,6 +152,16 @@ void ShaderEditWindow::Render() Settings::application.widget.shader_editor = false; if (ImGui::BeginMenu(IMGUI_TITLE_SHADEREDITOR)) { + // Menu entry to allow creating a custom filter + if (ImGui::MenuItem(ICON_FA_SHARE_SQUARE " Clone source & add filter", + nullptr, nullptr, cs != nullptr)) { + CloneSource *filteredclone = Mixer::manager().createSourceClone(); + filteredclone->setFilter(FrameBufferFilter::FILTER_IMAGE); + Mixer::manager().addSource ( filteredclone ); + UserInterface::manager().showPannel( Mixer::manager().numSource() ); + } + ImGui::Separator(); + // reload code from GPU if (ImGui::MenuItem( ICON_FA_REDO_ALT " Reload", nullptr, nullptr, current_ != nullptr)) { // force reload @@ -189,13 +202,6 @@ void ShaderEditWindow::Render() if (ImGui::MenuItem( ICON_FA_EXTERNAL_LINK_ALT " Browse shadertoy.com")) SystemToolkit::open("https://www.shadertoy.com/"); - // Enable/Disable editor options - ImGui::Separator(); - ImGui::MenuItem( ICON_FA_UNDERLINE " Show Shader Inputs", nullptr, &show_shader_inputs_); - bool ws = _editor.IsShowingWhitespaces(); - if (ImGui::MenuItem( ICON_FA_ELLIPSIS_H " Show whitespace", nullptr, &ws)) - _editor.SetShowWhitespaces(ws); - // output manager menu ImGui::Separator(); bool pinned = Settings::application.widget.shader_editor_view == Settings::application.current_view; @@ -231,6 +237,13 @@ void ShaderEditWindow::Render() if (ImGui::MenuItem( MENU_SELECTALL, SHORTCUT_SELECTALL, nullptr, _editor.GetText().size() > 1 )) _editor.SetSelection(TextEditor::Coordinates(), TextEditor::Coordinates(_editor.GetTotalLines(), 0)); + // Enable/Disable editor options + ImGui::Separator(); + ImGui::MenuItem( ICON_FA_UNDERLINE " Show Shader Inputs", nullptr, &show_shader_inputs_); + bool ws = _editor.IsShowingWhitespaces(); + if (ImGui::MenuItem( ICON_FA_ELLIPSIS_H " Show whitespace", nullptr, &ws)) + _editor.SetShowWhitespaces(ws); + ImGui::EndMenu(); } @@ -301,11 +314,9 @@ void ShaderEditWindow::Render() else { ImageFilter *i = nullptr; - // get current clone source - Source *s = Mixer::manager().currentSource(); // if there is a current source - if (s != nullptr) { - CloneSource *c = dynamic_cast(s); + if (cs != nullptr) { + CloneSource *c = dynamic_cast(cs); // if the current source is a clone if ( c != nullptr ) { FrameBufferFilter *f = c->filter(); @@ -325,6 +336,7 @@ void ShaderEditWindow::Render() if (i == nullptr) { status_ = "-"; _editor.SetText(""); + _editor.SetReadOnly(true); current_ = nullptr; } } @@ -347,10 +359,13 @@ void ShaderEditWindow::Render() _editor.SetText( filters_[i].code().first ); _editor.SetReadOnly(false); _editor.SetColorizerEnable(true); - status_ = "Ready."; + status_ = "Ready"; } // cancel edit clone else { + // possibility that source was removed + g_printerr("cancel edit clone %ld\n", current_); + // disable editor _editor.SetReadOnly(true); _editor.SetColorizerEnable(false); diff --git a/src/Source.cpp b/src/Source.cpp index 29495ce..1e952c8 100644 --- a/src/Source.cpp +++ b/src/Source.cpp @@ -948,7 +948,7 @@ void Source::update(float dt) if (ref_source != nullptr) { if (ref_source->ready()) // set mask texture to mask source - blendingshader_->mask_texture = ref_source->frame()->texture(); + blendingshader_->secondary_texture = ref_source->frame()->texture(); else // retry for when source will be ready need_update_ |= SourceUpdate_Mask; @@ -964,7 +964,7 @@ void Source::update(float dt) masksurface_->draw(glm::identity(), maskbuffer_->projection()); maskbuffer_->end(); // set mask texture to mask buffer - blendingshader_->mask_texture = maskbuffer_->texture(); + blendingshader_->secondary_texture = maskbuffer_->texture(); } } diff --git a/src/TextureView.cpp b/src/TextureView.cpp index 894cced..7d2eaf7 100644 --- a/src/TextureView.cpp +++ b/src/TextureView.cpp @@ -535,7 +535,7 @@ bool TextureView::adjustBackground() shift_crop_.x *= edit_source_->frame()->aspectRatio(); preview_surface_->setTextureIndex(edit_source_->frame()->texture()); - preview_shader_->mask_texture = edit_source_->blendingShader()->mask_texture; + preview_shader_->secondary_texture = edit_source_->blendingShader()->secondary_texture; preview_surface_->scale_ = scale_crop_; preview_surface_->translation_ = shift_crop_; diff --git a/src/UserInterfaceManager.cpp b/src/UserInterfaceManager.cpp index 2f095c1..2fb505f 100644 --- a/src/UserInterfaceManager.cpp +++ b/src/UserInterfaceManager.cpp @@ -268,6 +268,9 @@ void UserInterface::handleKeyboard() if ( TryClose() ) Rendering::manager().close(); } + else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_F), false )) { + Rendering::manager().mainWindow().toggleFullscreen(); + } else if (ImGui::IsKeyPressed( Control::layoutKey(GLFW_KEY_O), false )) { // SHIFT + CTRL + O : reopen current session if (shift_modifier_active && !Mixer::manager().session()->filename().empty()) @@ -1119,6 +1122,9 @@ void UserInterface::showMenuWindows() if ( ImGui::MenuItem( MENU_INPUTS, SHORTCUT_INPUTS, &Settings::application.widget.inputs) ) UserInterface::manager().inputscontrol.setVisible(Settings::application.widget.inputs); + if ( ImGui::MenuItem( MENU_SHADEREDITOR, SHORTCUT_SHADEREDITOR, &Settings::application.widget.shader_editor) ) + UserInterface::manager().shadercontrol.setVisible(Settings::application.widget.shader_editor); + // Show Help ImGui::MenuItem( MENU_HELP, SHORTCUT_HELP, &Settings::application.widget.help ); // Show Logs @@ -2514,7 +2520,6 @@ void UserInterface::RenderHelp() if (ImGui::CollapsingHeader("Views")) { - ImGui::Columns(2, "viewscolumn", false); // 4-ways, with border ImGui::SetColumnWidth(0, width_column0); ImGui::PushTextWrapPos(width_window ); @@ -2543,7 +2548,7 @@ void UserInterface::RenderHelp() ImGui::PopTextWrapPos(); } - if (ImGui::CollapsingHeader("Windows")) + if (ImGui::CollapsingHeader("Tools")) { ImGui::Columns(2, "windowcolumn", false); // 4-ways, with border ImGui::SetColumnWidth(0, width_column0); @@ -2577,6 +2582,62 @@ void UserInterface::RenderHelp() ImGui::PopTextWrapPos(); } + if (ImGui::CollapsingHeader("Files")) + { + { + float H = ImGui::GetFrameHeightWithSpacing(); + ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_MenuBar; + ImGui::PushStyleVar(ImGuiStyleVar_ChildRounding, 5.0f); + + ImGui::BeginChild("PlaylistHelp", ImVec2(0, 10.f * H), true, window_flags); + if (ImGui::BeginMenuBar()) { + ImGuiToolkit::Icon(4, 8); + ImGui::Text (" Playlist"); + ImGui::EndMenuBar(); + } + + { + ImGui::BeginChild("SessionHelp", ImVec2(0, 7.f * H), true, window_flags); + if (ImGui::BeginMenuBar()) { + ImGuiToolkit::Icon(7, 1); + ImGui::Text (" Session"); + ImGui::EndMenuBar(); + } + + { + ImGui::BeginChild("SourceHelp", ImVec2(0, 4.f * H), true, window_flags); + if (ImGui::BeginMenuBar()) { + ImGuiToolkit::Icon(14, 11); + ImGui::Text ("Source"); + ImGui::EndMenuBar(); + } + + ImGui::BulletText("Video, image & session files"); + ImGui::BulletText("Image sequence (image files)"); + ImGui::BulletText("Input devices & streams (e.g. webcams)"); + ImGui::BulletText("Patterns & generated graphics (e.g. text)"); + ImGui::EndChild(); + } + + ImGui::PushTextWrapPos(width_window - 10.f); + ImGui::Spacing(); + ImGui::Text ("A session contains several sources mixed together and keeps previous versions. " + "It is saved in a .mix file."); + ImGui::PopTextWrapPos(); + ImGui::EndChild(); + } + + ImGui::PushTextWrapPos(width_window - 10.f); + ImGui::Spacing(); + ImGui::Text ("A playlist keeps a list of sessions (or lists the .mix files in a folder) " + "for smooth transitions between files."); + ImGui::PopTextWrapPos(); + ImGui::EndChild(); + + ImGui::PopStyleVar(); + } + } + if (ImGui::CollapsingHeader("Sources")) { ImGui::Columns(2, "sourcecolumn", false); // 4-ways, with border @@ -3260,6 +3321,7 @@ void Navigator::Render() if (!std::get<0>(tooltip).empty()) { // pseudo timeout for showing tooltip if (_timeout_tooltip > IMGUI_TOOLTIP_TIMEOUT) { + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f)); // if a pointer to a Source is provided in tupple Source *_s = std::get<2>(tooltip); if (_s != nullptr) { @@ -3287,7 +3349,7 @@ void Navigator::Render() // otherwise just show a standard tooltip [action - shortcut key] else ImGuiToolkit::ToolTip(std::get<0>(tooltip).c_str(), std::get<1>(tooltip).c_str()); - + ImGui::PopStyleVar(); } else ++_timeout_tooltip; @@ -3499,16 +3561,13 @@ void Navigator::RenderSourcePannel(Source *s, const ImVec2 &iconsize) if ( s->failed() ) { ImGuiToolkit::ButtonDisabled( ICON_FA_SHARE_SQUARE " Clone & Filter", ImVec2(ImGui::GetContentRegionAvail().x, 0)); } - else if ( ImGui::Button( ICON_FA_SHARE_SQUARE " Clone & Filter", ImVec2(ImGui::GetContentRegionAvail().x, 0)) ) - Mixer::manager().addSource ( Mixer::manager().createSourceClone() ); + else if ( ImGui::Button( ICON_FA_SHARE_SQUARE " Clone & Filter", ImVec2(ImGui::GetContentRegionAvail().x, 0)) ) { + Mixer::manager().addSource ( (Source *) Mixer::manager().createSourceClone() ); + UserInterface::manager().showPannel( Mixer::manager().numSource() ); + } // replace button - if ( s->cloned() ) { - ImGuiToolkit::ButtonDisabled( ICON_FA_PLUS_SQUARE " Replace", ImVec2(ImGui::GetContentRegionAvail().x, 0)); - if (ImGui::IsItemHovered()) - ImGuiToolkit::ToolTip("Cannot replace if source is cloned"); - } - else if ( ImGui::Button( ICON_FA_PLUS_SQUARE " Replace", ImVec2(ImGui::GetContentRegionAvail().x, 0)) ) { + if ( ImGui::Button( ICON_FA_PLUS_SQUARE " Replace", ImVec2(ImGui::GetContentRegionAvail().x, 0)) ) { // prepare panel for new source of same type MediaSource *file = dynamic_cast(s); MultiFileSource *sequence = dynamic_cast(s); @@ -4667,7 +4726,7 @@ void Navigator::RenderMainPannelSession() width = height * se->frame()->aspectRatio() * ( se->frame()->projectionSize().x / se->frame()->projectionSize().y); } // centered image - ImGui::SetCursorPos( ImVec2(pos.x + 0.5f * (preview_width-width), pos.y + 0.5f * (preview_height-height-space)) ); + ImGui::SetCursorPos( ImVec2(pos.x + 0.5f * (preview_width-width), pos.y) ); ImGui::Image((void*)(uintptr_t) se->frame()->texture(), ImVec2(width, height)); // right side options for session @@ -4721,6 +4780,7 @@ void Navigator::RenderMainPannelSession() if (_thumbnail != nullptr) _session_thumbnail.fill( _thumbnail ); } + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f)); ImGui::BeginTooltip(); if (_session_thumbnail.filled()) { _session_thumbnail.Render(230); @@ -4730,6 +4790,7 @@ void Navigator::RenderMainPannelSession() ImGui::Text(" Automatic thumbnail "); } ImGui::EndTooltip(); + ImGui::PopStyleVar(); } } @@ -4884,10 +4945,12 @@ void Navigator::RenderMainPannelSession() _undo_thumbnail.reset(); } // draw thumbnail in tooltip + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f)); ImGui::BeginTooltip(); _undo_thumbnail.Render(size.x); ImGui::Text("%s", text.c_str()); ImGui::EndTooltip(); + ImGui::PopStyleVar(); ++count_over; // prevents display twice on item overlap } @@ -5002,10 +5065,12 @@ void Navigator::RenderMainPannelSession() current_over = _over; } // draw thumbnail in tooltip + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f)); ImGui::BeginTooltip(); _snap_thumbnail.Render(size.x); ImGui::Text("%s", _snap_date.c_str()); ImGui::EndTooltip(); + ImGui::PopStyleVar(); ++count_over; // prevents display twice on item overlap } } @@ -5458,6 +5523,7 @@ void Navigator::RenderMainPannelPlaylist() if ( !_file_info.empty()) { + ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(8.f, 8.f)); ImGui::BeginTooltip(); ImVec2 p_ = ImGui::GetCursorScreenPos(); _file_thumbnail.Render(240); @@ -5467,6 +5533,7 @@ void Navigator::RenderMainPannelPlaylist() ImGui::Text(ICON_FA_TAG); } ImGui::EndTooltip(); + ImGui::PopStyleVar(); } } @@ -6056,7 +6123,7 @@ void Navigator::RenderMainPannel(const ImVec2 &iconsize) ImGui::EndMenu(); } ImGui::SetCursorPos( ImVec2( pannel_width_ IMGUI_RIGHT_ALIGN, IMGUI_TOP_ALIGN + 2.f * ImGui::GetTextLineHeightWithSpacing()) ); - if (ImGui::BeginMenu("View")) { + if (ImGui::BeginMenu("Tools")) { UserInterface::manager().showMenuWindows(); ImGui::EndMenu(); } diff --git a/src/defines.h b/src/defines.h index 302e220..ac03bbc 100644 --- a/src/defines.h +++ b/src/defines.h @@ -232,14 +232,15 @@ #define MENU_TIMER ICON_FA_CLOCK " Timer " #define TOOLTIP_TIMER "Timer " #define SHORTCUT_TIMER CTRL_MOD "T" -#define MENU_INPUTS ICON_FA_HAND_PAPER " Inputs mapping " +#define MENU_INPUTS ICON_FA_HAND_PAPER " Input mapping " #define TOOLTIP_INPUTS "Inputs mapping " #define SHORTCUT_INPUTS CTRL_MOD "I" +#define MENU_SHADEREDITOR ICON_FA_CODE " Shader Editor " #define TOOLTIP_SHADEREDITOR "Shader Editor " #define SHORTCUT_SHADEREDITOR CTRL_MOD "E" #define TOOLTIP_FULLSCREEN "Fullscreen " #define SHORTCUT_FULLSCREEN CTRL_MOD "F" -#define TOOLTIP_MAIN "Main menu " +#define TOOLTIP_MAIN "Home panel " #define SHORTCUT_MAIN "HOME" #define TOOLTIP_NEW_SOURCE "Insert " #define SHORTCUT_NEW_SOURCE "INS"