mirror of
https://github.com/ocornut/imgui.git
synced 2026-05-30 05:45:29 +08:00
Demo: Documents: refactor + add Renaming option. (#7233)
This commit is contained in:
@@ -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
|
||||||
|
|||||||
+107
-74
@@ -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();
|
||||||
|
|||||||
Reference in New Issue
Block a user