Demo: Documents: refactor + add Renaming option. (#7233)

features/potocpav-newer-lines-2
ocornut ago%!(EXTRA string=1 year)
parent c150ad50c4
commit 374b9a7fb4
  1. 1
      docs/CHANGELOG.txt
  2. 181
      imgui_demo.cpp

@ -89,6 +89,7 @@ Other changes:
Broken during a refactor refactor for 1.89. Holding Shift/R1 to speed up wasn't broken. Broken during a refactor refactor for 1.89. Holding Shift/R1 to speed up wasn't broken.
- Tables: fixed cell background of fully clipped row overlapping with header. (#7575, #7041) [@prabuinet] - Tables: fixed cell background of fully clipped row overlapping with header. (#7575, #7041) [@prabuinet]
- Demo: Added "Inputs & Focus -> Shortcuts" section. (#456, #2637) - Demo: Added "Inputs & Focus -> Shortcuts" section. (#456, #2637)
- Demo: Documents: Added shortcuts and renaming tabs/documents. (#7233)
- Examples: Win32+DX9,DX10,DX11,DX12: rework main loop to handle minimization and screen - Examples: Win32+DX9,DX10,DX11,DX12: rework main loop to handle minimization and screen
locking without burning resources by running unthrottled code. (#2496, #3907, #6308, #7615) locking without burning resources by running unthrottled code. (#2496, #3907, #6308, #7615)
- Backends: all backends + demo now call IMGUI_CHECKVERSION() to verify ABI compatibility between caller - Backends: all backends + demo now call IMGUI_CHECKVERSION() to verify ABI compatibility between caller

@ -8422,51 +8422,85 @@ static void ShowExampleAppCustomRendering(bool* p_open)
// Simplified structure to mimic a Document model // Simplified structure to mimic a Document model
struct MyDocument struct MyDocument
{ {
const char* Name; // Document title char Name[32]; // Document title
int UID; // Unique ID (necessary as we can change title)
bool Open; // Set when open (we keep an array of all available documents to simplify demo code!) bool Open; // Set when open (we keep an array of all available documents to simplify demo code!)
bool OpenPrev; // Copy of Open from last update. bool OpenPrev; // Copy of Open from last update.
bool Dirty; // Set when the document has been modified bool Dirty; // Set when the document has been modified
bool WantClose; // Set when the document
ImVec4 Color; // An arbitrary variable associated to the document ImVec4 Color; // An arbitrary variable associated to the document
MyDocument(const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f)) MyDocument(int uid, const char* name, bool open = true, const ImVec4& color = ImVec4(1.0f, 1.0f, 1.0f, 1.0f))
{ {
Name = name; UID = uid;
snprintf(Name, sizeof(Name), "%s", name);
Open = OpenPrev = open; Open = OpenPrev = open;
Dirty = false; Dirty = false;
WantClose = false;
Color = color; Color = color;
} }
void DoOpen() { Open = true; } void DoOpen() { Open = true; }
void DoQueueClose() { WantClose = true; }
void DoForceClose() { Open = false; Dirty = false; } void DoForceClose() { Open = false; Dirty = false; }
void DoSave() { Dirty = false; } void DoSave() { Dirty = false; }
};
struct ExampleAppDocuments
{
ImVector<MyDocument> Documents;
ImVector<MyDocument*> CloseQueue;
MyDocument* RenamingDoc = NULL;
bool RenamingStarted = false;
ExampleAppDocuments()
{
Documents.push_back(MyDocument(0, "Lettuce", true, ImVec4(0.4f, 0.8f, 0.4f, 1.0f)));
Documents.push_back(MyDocument(1, "Eggplant", true, ImVec4(0.8f, 0.5f, 1.0f, 1.0f)));
Documents.push_back(MyDocument(2, "Carrot", true, ImVec4(1.0f, 0.8f, 0.5f, 1.0f)));
Documents.push_back(MyDocument(3, "Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f)));
Documents.push_back(MyDocument(4, "A Rather Long Title", false, ImVec4(0.4f, 0.8f, 0.8f, 1.0f)));
Documents.push_back(MyDocument(5, "Some Document", false, ImVec4(0.8f, 0.8f, 1.0f, 1.0f)));
}
// As we allow to change document name, we append a never-changing document ID so tabs are stable
void GetTabName(MyDocument* doc, char* out_buf, size_t out_buf_size)
{
snprintf(out_buf, out_buf_size, "%s###doc%d", doc->Name, doc->UID);
}
// Display placeholder contents for the Document // Display placeholder contents for the Document
static void DisplayContents(MyDocument* doc) void DisplayDocContents(MyDocument* doc)
{ {
ImGui::PushID(doc); ImGui::PushID(doc);
ImGui::Text("Document \"%s\"", doc->Name); ImGui::Text("Document \"%s\"", doc->Name);
ImGui::PushStyleColor(ImGuiCol_Text, doc->Color); ImGui::PushStyleColor(ImGuiCol_Text, doc->Color);
ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."); ImGui::TextWrapped("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.");
ImGui::PopStyleColor(); ImGui::PopStyleColor();
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_R, ImGuiInputFlags_Tooltip);
if (ImGui::Button("Rename.."))
{
RenamingDoc = doc;
RenamingStarted = true;
}
ImGui::SameLine();
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_M, ImGuiInputFlags_Tooltip); ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_M, ImGuiInputFlags_Tooltip);
if (ImGui::Button("Modify")) if (ImGui::Button("Modify"))
doc->Dirty = true; doc->Dirty = true;
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_S, ImGuiInputFlags_Tooltip); ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_S, ImGuiInputFlags_Tooltip);
if (ImGui::Button("Save")) if (ImGui::Button("Save"))
doc->DoSave(); doc->DoSave();
ImGui::SameLine(); ImGui::SameLine();
ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_W, ImGuiInputFlags_Tooltip); ImGui::SetNextItemShortcut(ImGuiMod_Ctrl | ImGuiKey_W, ImGuiInputFlags_Tooltip);
if (ImGui::Button("Close")) if (ImGui::Button("Close"))
doc->DoQueueClose(); CloseQueue.push_back(doc);
ImGui::ColorEdit3("color", &doc->Color.x); // Useful to test drag and drop and hold-dragged-to-open-tab behavior. ImGui::ColorEdit3("color", &doc->Color.x); // Useful to test drag and drop and hold-dragged-to-open-tab behavior.
ImGui::PopID(); ImGui::PopID();
} }
// Display context menu for the Document // Display context menu for the Document
static void DisplayContextMenu(MyDocument* doc) void DisplayDocContextMenu(MyDocument* doc)
{ {
if (!ImGui::BeginPopupContextItem()) if (!ImGui::BeginPopupContextItem())
return; return;
@ -8475,45 +8509,32 @@ struct MyDocument
sprintf(buf, "Save %s", doc->Name); sprintf(buf, "Save %s", doc->Name);
if (ImGui::MenuItem(buf, "Ctrl+S", false, doc->Open)) if (ImGui::MenuItem(buf, "Ctrl+S", false, doc->Open))
doc->DoSave(); doc->DoSave();
if (ImGui::MenuItem("Rename...", "Ctrl+R", false, doc->Open))
RenamingDoc = doc;
if (ImGui::MenuItem("Close", "Ctrl+W", false, doc->Open)) if (ImGui::MenuItem("Close", "Ctrl+W", false, doc->Open))
doc->DoQueueClose(); CloseQueue.push_back(doc);
ImGui::EndPopup(); ImGui::EndPopup();
} }
};
struct ExampleAppDocuments // [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface.
{ // If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo,
ImVector<MyDocument> Documents; // as opposed to clicking on the regular tab closing button) and stops being submitted, it will take a frame for
// the tab bar to notice its absence. During this frame there will be a gap in the tab bar, and if the tab that has
ExampleAppDocuments() // disappeared was the selected one, the tab bar will report no selected tab during the frame. This will effectively
// give the impression of a flicker for one frame.
// We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch.
// Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag.
void NotifyOfDocumentsClosedElsewhere()
{ {
Documents.push_back(MyDocument("Lettuce", true, ImVec4(0.4f, 0.8f, 0.4f, 1.0f))); for (MyDocument& doc : Documents)
Documents.push_back(MyDocument("Eggplant", true, ImVec4(0.8f, 0.5f, 1.0f, 1.0f))); {
Documents.push_back(MyDocument("Carrot", true, ImVec4(1.0f, 0.8f, 0.5f, 1.0f))); if (!doc.Open && doc.OpenPrev)
Documents.push_back(MyDocument("Tomato", false, ImVec4(1.0f, 0.3f, 0.4f, 1.0f))); ImGui::SetTabItemClosed(doc.Name);
Documents.push_back(MyDocument("A Rather Long Title", false)); doc.OpenPrev = doc.Open;
Documents.push_back(MyDocument("Some Document", false)); }
} }
}; };
// [Optional] Notify the system of Tabs/Windows closure that happened outside the regular tab interface.
// If a tab has been closed programmatically (aka closed from another source such as the Checkbox() in the demo,
// as opposed to clicking on the regular tab closing button) and stops being submitted, it will take a frame for
// the tab bar to notice its absence. During this frame there will be a gap in the tab bar, and if the tab that has
// disappeared was the selected one, the tab bar will report no selected tab during the frame. This will effectively
// give the impression of a flicker for one frame.
// We call SetTabItemClosed() to manually notify the Tab Bar or Docking system of removed tabs to avoid this glitch.
// Note that this completely optional, and only affect tab bars with the ImGuiTabBarFlags_Reorderable flag.
static void NotifyOfDocumentsClosedElsewhere(ExampleAppDocuments& app)
{
for (MyDocument& doc : app.Documents)
{
if (!doc.Open && doc.OpenPrev)
ImGui::SetTabItemClosed(doc.Name);
doc.OpenPrev = doc.Open;
}
}
void ShowExampleAppDocuments(bool* p_open) void ShowExampleAppDocuments(bool* p_open)
{ {
static ExampleAppDocuments app; static ExampleAppDocuments app;
@ -8547,7 +8568,7 @@ void ShowExampleAppDocuments(bool* p_open)
} }
if (ImGui::MenuItem("Close All Documents", NULL, false, open_count > 0)) if (ImGui::MenuItem("Close All Documents", NULL, false, open_count > 0))
for (MyDocument& doc : app.Documents) for (MyDocument& doc : app.Documents)
doc.DoQueueClose(); app.CloseQueue.push_back(&doc);
if (ImGui::MenuItem("Exit") && p_open) if (ImGui::MenuItem("Exit") && p_open)
*p_open = false; *p_open = false;
ImGui::EndMenu(); ImGui::EndMenu();
@ -8586,7 +8607,7 @@ void ShowExampleAppDocuments(bool* p_open)
if (ImGui::BeginTabBar("##tabs", tab_bar_flags)) if (ImGui::BeginTabBar("##tabs", tab_bar_flags))
{ {
if (opt_reorderable) if (opt_reorderable)
NotifyOfDocumentsClosedElsewhere(app); app.NotifyOfDocumentsClosedElsewhere();
// [DEBUG] Stress tests // [DEBUG] Stress tests
//if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1; // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on. //if ((ImGui::GetFrameCount() % 30) == 0) docs[1].Open ^= 1; // [DEBUG] Automatically show/hide a tab. Test various interactions e.g. dragging with this on.
@ -8598,20 +8619,23 @@ void ShowExampleAppDocuments(bool* p_open)
if (!doc.Open) if (!doc.Open)
continue; continue;
// As we allow to change document name, we append a never-changing document id so tabs are stable
char doc_name_buf[64];
app.GetTabName(&doc, doc_name_buf, sizeof(doc_name_buf));
ImGuiTabItemFlags tab_flags = (doc.Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0); ImGuiTabItemFlags tab_flags = (doc.Dirty ? ImGuiTabItemFlags_UnsavedDocument : 0);
bool visible = ImGui::BeginTabItem(doc.Name, &doc.Open, tab_flags); bool visible = ImGui::BeginTabItem(doc_name_buf, &doc.Open, tab_flags);
// Cancel attempt to close when unsaved add to save queue so we can display a popup. // Cancel attempt to close when unsaved add to save queue so we can display a popup.
if (!doc.Open && doc.Dirty) if (!doc.Open && doc.Dirty)
{ {
doc.Open = true; doc.Open = true;
doc.DoQueueClose(); app.CloseQueue.push_back(&doc);
} }
MyDocument::DisplayContextMenu(&doc); app.DisplayDocContextMenu(&doc);
if (visible) if (visible)
{ {
MyDocument::DisplayContents(&doc); app.DisplayDocContents(&doc);
ImGui::EndTabItem(); ImGui::EndTabItem();
} }
} }
@ -8620,33 +8644,44 @@ void ShowExampleAppDocuments(bool* p_open)
} }
} }
// Update closing queue // Display renaming UI
static ImVector<MyDocument*> close_queue; if (app.RenamingDoc != NULL)
if (close_queue.empty())
{ {
// Close queue is locked once we started a popup if (app.RenamingStarted)
for (MyDocument& doc : app.Documents) ImGui::OpenPopup("Rename");
if (doc.WantClose) if (ImGui::BeginPopup("Rename"))
{
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 30);
if (ImGui::InputText("###Name", app.RenamingDoc->Name, IM_ARRAYSIZE(app.RenamingDoc->Name), ImGuiInputTextFlags_EnterReturnsTrue))
{ {
doc.WantClose = false; ImGui::CloseCurrentPopup();
close_queue.push_back(&doc); app.RenamingDoc = NULL;
} }
if (app.RenamingStarted)
ImGui::SetKeyboardFocusHere(-1);
ImGui::EndPopup();
}
else
{
app.RenamingDoc = NULL;
}
app.RenamingStarted = false;
} }
// Display closing confirmation UI // Display closing confirmation UI
if (!close_queue.empty()) if (!app.CloseQueue.empty())
{ {
int close_queue_unsaved_documents = 0; int close_queue_unsaved_documents = 0;
for (int n = 0; n < close_queue.Size; n++) for (int n = 0; n < app.CloseQueue.Size; n++)
if (close_queue[n]->Dirty) if (app.CloseQueue[n]->Dirty)
close_queue_unsaved_documents++; close_queue_unsaved_documents++;
if (close_queue_unsaved_documents == 0) if (close_queue_unsaved_documents == 0)
{ {
// Close documents when all are unsaved // Close documents when all are unsaved
for (int n = 0; n < close_queue.Size; n++) for (int n = 0; n < app.CloseQueue.Size; n++)
close_queue[n]->DoForceClose(); app.CloseQueue[n]->DoForceClose();
close_queue.clear(); app.CloseQueue.clear();
} }
else else
{ {
@ -8657,37 +8692,35 @@ void ShowExampleAppDocuments(bool* p_open)
ImGui::Text("Save change to the following items?"); ImGui::Text("Save change to the following items?");
float item_height = ImGui::GetTextLineHeightWithSpacing(); float item_height = ImGui::GetTextLineHeightWithSpacing();
if (ImGui::BeginChild(ImGui::GetID("frame"), ImVec2(-FLT_MIN, 6.25f * item_height), ImGuiChildFlags_FrameStyle)) if (ImGui::BeginChild(ImGui::GetID("frame"), ImVec2(-FLT_MIN, 6.25f * item_height), ImGuiChildFlags_FrameStyle))
{ for (MyDocument* doc : app.CloseQueue)
for (int n = 0; n < close_queue.Size; n++) if (doc->Dirty)
if (close_queue[n]->Dirty) ImGui::Text("%s", doc->Name);
ImGui::Text("%s", close_queue[n]->Name);
}
ImGui::EndChild(); ImGui::EndChild();
ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f); ImVec2 button_size(ImGui::GetFontSize() * 7.0f, 0.0f);
if (ImGui::Button("Yes", button_size)) if (ImGui::Button("Yes", button_size))
{ {
for (int n = 0; n < close_queue.Size; n++) for (MyDocument* doc : app.CloseQueue)
{ {
if (close_queue[n]->Dirty) if (doc->Dirty)
close_queue[n]->DoSave(); doc->DoSave();
close_queue[n]->DoForceClose(); doc->DoForceClose();
} }
close_queue.clear(); app.CloseQueue.clear();
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("No", button_size)) if (ImGui::Button("No", button_size))
{ {
for (int n = 0; n < close_queue.Size; n++) for (MyDocument* doc : app.CloseQueue)
close_queue[n]->DoForceClose(); doc->DoForceClose();
close_queue.clear(); app.CloseQueue.clear();
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
ImGui::SameLine(); ImGui::SameLine();
if (ImGui::Button("Cancel", button_size)) if (ImGui::Button("Cancel", button_size))
{ {
close_queue.clear(); app.CloseQueue.clear();
ImGui::CloseCurrentPopup(); ImGui::CloseCurrentPopup();
} }
ImGui::EndPopup(); ImGui::EndPopup();

Loading…
Cancel
Save