Auto collapse of Shader Editor window

Reduces height of shader editor when there is no shader to edit. Cleanup of Workspace window implementation.
This commit is contained in:
Bruno Herbelin
2025-03-29 18:46:08 +01:00
parent 1787739128
commit 15eb546c3d
3 changed files with 178 additions and 118 deletions

View File

@@ -124,6 +124,7 @@ ShaderEditWindow::ShaderEditWindow() : WorkspaceWindow("Shader"), _cs_id(0), cur
// status
status_ = "-";
}
void ShaderEditWindow::setVisible(bool on)
@@ -139,6 +140,7 @@ void ShaderEditWindow::setVisible(bool on)
Settings::application.widget.shader_editor_view = -1;
Settings::application.widget.shader_editor = on;
}
bool ShaderEditWindow::Visible() const
@@ -188,7 +190,6 @@ void ShaderEditWindow::BuildAll()
}
}
void ShaderEditWindow::Render()
{
static DialogToolkit::OpenFileDialog selectcodedialog("Open GLSL shader code",
@@ -198,18 +199,54 @@ void ShaderEditWindow::Render()
"Text files",
{"*.glsl", "*.fs", "*.txt"} );
ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
std::string file_to_build_ = "";
Source *cs = Mixer::manager().currentSource();
// garbage collection
if ( !filters_.empty() && Mixer::manager().session()->numSources() < 1 )
{
// empty list of edited filter when session empty
filters_.clear();
current_ = nullptr;
// clear editor text and recent file
_editor.SetText("");
Settings::application.recentShaderCode.assign("");
}
// File dialog Export code gives a filename to save to
if (exportcodedialog.closed() && !exportcodedialog.path().empty()) {
// set shader program to be a file
file_to_build_ = exportcodedialog.path();
// save the current content of editor into given file
saveEditorText(file_to_build_);
}
// File dialog select code gives a filename to open
if (selectcodedialog.closed() && !selectcodedialog.path().empty()) {
// set shader program to be a file
file_to_build_ = selectcodedialog.path();
// read file and display content in editor
_editor.SetText(SystemToolkit::get_text_content(file_to_build_));
}
// AUTO COLLAPSE
setCollapsed(current_ == nullptr);
// Render window
ImGui::SetNextWindowSize(ImVec2(800, 600), ImGuiCond_FirstUseEver);
if ( !ImGui::Begin(name_, &Settings::application.widget.shader_editor,
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse ))
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_MenuBar |
ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoCollapse))
{
ImGui::End();
return;
}
std::string file_to_build_ = "";
Source *cs = Mixer::manager().currentSource();
// menu (no title bar)
if (ImGui::BeginMenuBar())
{
@@ -387,37 +424,25 @@ void ShaderEditWindow::Render()
ImGui::EndMenuBar();
}
// garbage collection
if ( !filters_.empty() && Mixer::manager().session()->numSources() < 1 )
{
// empty list of edited filter when session empty
filters_.clear();
current_ = nullptr;
// clear editor text and recent file
_editor.SetText("");
Settings::application.recentShaderCode.assign("");
}
// File dialog Export code gives a filename to save to
if (exportcodedialog.closed() && !exportcodedialog.path().empty()) {
// set shader program to be a file
file_to_build_ = exportcodedialog.path();
// save the current content of editor into given file
saveEditorText(file_to_build_);
}
// File dialog select code gives a filename to open
if (selectcodedialog.closed() && !selectcodedialog.path().empty()) {
// set shader program to be a file
file_to_build_ = selectcodedialog.path();
// read file and display content in editor
_editor.SetText(SystemToolkit::get_text_content(file_to_build_));
// the TextEditor captures keyboard focus from the main imgui context
// so UserInterface::handleKeyboard cannot capture this event:
// reading key press before render bypasses this problem
const ImGuiIO& io = ImGui::GetIO();
if (io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl) {
// special case for 'CTRL + B' keyboard shortcut
if (ImGui::IsKeyPressed(io.KeyMap[ImGuiKey_A] + 1))
BuildShader();
// special case for 'CTRL + S' keyboard shortcut
if (ImGui::IsKeyPressed(io.KeyMap[ImGuiKey_V] - 3)) {
// if present, save the program file with current content of editor
if (!filters_[current_].filename().empty())
saveEditorText(filters_[current_].filename());
// save session
Mixer::manager().save();
}
}
// compile
if (current_ != nullptr && !file_to_build_.empty()) {
// ok editor
@@ -446,7 +471,6 @@ void ShaderEditWindow::Render()
}
// not compiling
else {
// if there is a current source
if (cs != nullptr) {
// if current source is different from previous one
@@ -519,7 +543,6 @@ void ShaderEditWindow::Render()
_cs_id = 0;
status_ = "-";
}
}
// render status message
@@ -527,10 +550,11 @@ void ShaderEditWindow::Render()
ImGui::Text("Status: %s", status_.c_str());
// Right-align on same line than status
const float w = ImGui::GetContentRegionAvail().x - ImGui::GetTextLineHeight();
// Display name of program for embedded code
if (current_ != nullptr) {
const float w = ImGui::GetContentRegionAvail().x - ImGui::GetTextLineHeight();
ImVec2 txtsize = ImGui::CalcTextSize(Settings::application.recentShaderCode.path.c_str(), NULL);
ImGui::SameLine(w - txtsize.x - IMGUI_SAME_LINE, 0);
@@ -569,6 +593,14 @@ void ShaderEditWindow::Render()
ImGui::PopStyleVar(1);
}
}
else {
ImVec2 txtsize = ImGui::CalcTextSize("No shader", NULL);
ImGui::SameLine(w - txtsize.x - IMGUI_SAME_LINE, 0);
// right-aligned in italics and greyed out
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.8, 0.8, 0.8, 0.9f));
ImGui::Text("No Shader");
ImGui::PopStyleColor(1);
}
ImGui::PopFont();
@@ -576,7 +608,7 @@ void ShaderEditWindow::Render()
ImGuiToolkit::PushFont(ImGuiToolkit::FONT_MONO);
// render shader input
if (show_shader_inputs_) {
if (current_ != nullptr && show_shader_inputs_) {
ImGuiTextBuffer info;
info.append(FilteringProgram::getFilterCodeInputs().c_str());
@@ -610,26 +642,9 @@ void ShaderEditWindow::Render()
else
ImGui::Spacing();
// the TextEditor captures keyboard focus from the main imgui context
// so UserInterface::handleKeyboard cannot capture this event:
// reading key press before render bypasses this problem
const ImGuiIO& io = ImGui::GetIO();
if (io.ConfigMacOSXBehaviors ? io.KeySuper : io.KeyCtrl) {
// special case for 'CTRL + B' keyboard shortcut
if (ImGui::IsKeyPressed(io.KeyMap[ImGuiKey_A] + 1))
BuildShader();
// special case for 'CTRL + S' keyboard shortcut
if (ImGui::IsKeyPressed(io.KeyMap[ImGuiKey_V] - 3)) {
// if present, save the program file with current content of editor
if (!filters_[current_].filename().empty())
saveEditorText(filters_[current_].filename());
// save session
Mixer::manager().save();
}
}
// render main editor
_editor.Render("Shader Editor");
if (_editor.GetTotalLines()>1)
_editor.Render("Shader Editor");
ImGui::PopFont();

View File

@@ -31,21 +31,24 @@ std::list<WorkspaceWindow *> WorkspaceWindow::windows_;
struct ImGuiProperties
{
ImGuiWindow *ptr;
ImVec2 user_pos;
ImVec2 outside_pos;
float progress; // [0 1]
float target; // 1 go to outside, 0 go to user pos
bool animation; // need animation
bool animation; // need animation
bool resizing_workspace;
bool hidden;
bool collapsed;
ImVec2 user_pos;
ImVec2 user_size;
ImVec2 hidden_pos;
ImVec2 resized_pos;
ImGuiProperties ()
{
ptr = nullptr;
progress = 0.f;
target = 0.f;
animation = false;
resizing_workspace = false;
hidden = false;
collapsed = false;
}
};
@@ -69,32 +72,98 @@ void WorkspaceWindow::toggleClearRestoreWorkspace()
clearWorkspace();
}
void WorkspaceWindow::restoreWorkspace(bool instantaneous)
void WorkspaceWindow::setHidden(bool h, bool force)
{
if (clear_workspace_enabled) {
// hide if valid pointer and if status 'hidden' is changing (or forcing entry)
if (impl_ && impl_->ptr && ( impl_->hidden != h || force )) {
// set status
impl_->hidden = h;
// utility variables
const ImVec2 display_size = ImGui::GetIO().DisplaySize;
for(auto it = windows_.cbegin(); it != windows_.cend(); ++it) {
ImGuiProperties *impl = (*it)->impl_;
if (impl && impl->ptr)
{
float margin = (impl->ptr->MenuBarHeight() + impl->ptr->TitleBarHeight()) * 3.f;
impl->user_pos.x = CLAMP(impl->user_pos.x, -impl->ptr->SizeFull.x +margin, display_size.x -margin);
impl->user_pos.y = CLAMP(impl->user_pos.y, -impl->ptr->SizeFull.y +margin, display_size.y -margin);
if (instantaneous) {
impl->animation = false;
ImGui::SetWindowPos(impl->ptr, impl->user_pos);
}
else {
// remember outside position before move
impl->outside_pos = impl->ptr->Pos;
if (h) {
// remember user position before animation
impl_->user_pos = impl_->ptr->Pos;
// initialize animation
impl->progress = 1.f;
impl->target = 0.f;
impl->animation = true;
}
// distance to right side, top & bottom
float right = display_size.x - (impl_->ptr->Pos.x + impl_->ptr->SizeFull.x * 0.7);
float top = impl_->ptr->Pos.y;
float bottom = display_size.y - (impl_->ptr->Pos.y + impl_->ptr->SizeFull.y);
// hidden_pos starts from user position,
// + moved to closest border, with a margin to keep visible
impl_->hidden_pos = impl_->ptr->Pos;
float margin = (impl_->ptr->MenuBarHeight() + impl_->ptr->TitleBarHeight()) * 1.5f;
if (top < bottom && top < right)
impl_->hidden_pos.y = margin - impl_->ptr->SizeFull.y;
else if (right < top && right < bottom)
impl_->hidden_pos.x = display_size.x - margin;
else
impl_->hidden_pos.y = display_size.y - margin;
if (force) {
impl_->animation = false;
ImGui::SetWindowPos(impl_->ptr, impl_->hidden_pos);
}
else {
// initialize animation
impl_->progress = 0.f;
impl_->animation = true;
}
}
else {
// remember outside position before animation
impl_->hidden_pos = impl_->ptr->Pos;
float margin = (impl_->ptr->MenuBarHeight() + impl_->ptr->TitleBarHeight()) * 3.f;
impl_->user_pos.x = CLAMP(impl_->user_pos.x,
-impl_->ptr->SizeFull.x + margin,
display_size.x - margin);
impl_->user_pos.y = CLAMP(impl_->user_pos.y,
-impl_->ptr->SizeFull.y + margin,
display_size.y - margin);
if (force) {
impl_->animation = false;
// ImGui::SetWindowPos(impl_->ptr, impl_->user_pos);
}
else {
// initialize animation
impl_->progress = 1.f;
impl_->animation = true;
}
}
}
}
void WorkspaceWindow::setCollapsed(bool c)
{
// hide if valid pointer and if status 'collapsed' is changing
if (impl_ && impl_->ptr && impl_->collapsed != c && !impl_->hidden) {
impl_->collapsed = c;
ImVec2 s = impl_->ptr->SizeFull;
if (c) {
impl_->user_size = s;
s.y = impl_->ptr->MenuBarHeight() * 2.3f;
} else
s.y = impl_->user_size.y;
ImGui::SetWindowSize(impl_->ptr, s);
}
}
void WorkspaceWindow::restoreWorkspace(bool force)
{
if (clear_workspace_enabled || force) {
// const ImVec2 display_size = ImGui::GetIO().DisplaySize;
for(auto it = windows_.cbegin(); it != windows_.cend(); ++it) {
if (force)
(*it)->setCollapsed(false);
(*it)->setHidden(false, force);
}
}
clear_workspace_enabled = false;
@@ -103,36 +172,9 @@ void WorkspaceWindow::restoreWorkspace(bool instantaneous)
void WorkspaceWindow::clearWorkspace()
{
if (!clear_workspace_enabled) {
const ImVec2 display_size = ImGui::GetIO().DisplaySize;
// const ImVec2 display_size = ImGui::GetIO().DisplaySize;
for(auto it = windows_.cbegin(); it != windows_.cend(); ++it) {
ImGuiProperties *impl = (*it)->impl_;
if (impl && impl->ptr)
{
// remember user position before move
impl->user_pos = impl->ptr->Pos;
// init before decision
impl->outside_pos = impl->ptr->Pos;
// distance to right side, top & bottom
float right = display_size.x - (impl->ptr->Pos.x + impl->ptr->SizeFull.x * 0.7);
float top = impl->ptr->Pos.y;
float bottom = display_size.y - (impl->ptr->Pos.y + impl->ptr->SizeFull.y);
// move to closest border, with a margin to keep visible
float margin = (impl->ptr->MenuBarHeight() + impl->ptr->TitleBarHeight()) * 1.5f;
if (top < bottom && top < right)
impl->outside_pos.y = margin - impl->ptr->SizeFull.y;
else if (right < top && right < bottom)
impl->outside_pos.x = display_size.x - margin;
else
impl->outside_pos.y = display_size.y - margin;
// initialize animation
impl->progress = 0.f;
impl->target = 1.f;
impl->animation = true;
}
(*it)->setHidden(true);
}
}
clear_workspace_enabled = true;
@@ -180,17 +222,18 @@ void WorkspaceWindow::Update()
if ( Visible() ) {
// perform animation for clear/restore workspace
if (impl_->animation) {
const float target = impl_->hidden ? 1.f : 0.f;
// increment animation progress by small steps
impl_->progress += SIGN(impl_->target -impl_->progress) * 0.1f;
impl_->progress += SIGN(target -impl_->progress) * 0.1f;
// finished animation : apply full target position
if (ABS_DIFF(impl_->target, impl_->progress) < 0.05f) {
if (ABS_DIFF(target, impl_->progress) < 0.05f) {
impl_->animation = false;
ImVec2 pos = impl_->user_pos * (1.f -impl_->target) + impl_->outside_pos * impl_->target;
ImVec2 pos = impl_->user_pos * (1.f -target) + impl_->hidden_pos * target;
ImGui::SetWindowPos(impl_->ptr, pos);
}
// move window by interpolation between user position and outside target position
else {
ImVec2 pos = impl_->user_pos * (1.f -impl_->progress) + impl_->outside_pos * impl_->progress;
ImVec2 pos = impl_->user_pos * (1.f -impl_->progress) + impl_->hidden_pos * impl_->progress;
ImGui::SetWindowPos(impl_->ptr, pos);
}
}

View File

@@ -14,12 +14,14 @@ public:
static bool clear() { return clear_workspace_enabled; }
static void toggleClearRestoreWorkspace();
static void clearWorkspace();
static void restoreWorkspace(bool instantaneous = false);
static void restoreWorkspace(bool force = false);
static void notifyWorkspaceSizeChanged(int prev_width, int prev_height, int curr_width, int curr_height);
// for inherited classes
virtual void Update();
virtual bool Visible() const { return true; }
virtual void setHidden(bool h, bool force = false);
virtual void setCollapsed(bool c);
protected: