progress on fixing up gui code

This commit is contained in:
2026-05-12 15:20:34 -04:00
parent fb45b44824
commit c4e1cca66b
6 changed files with 198 additions and 197 deletions
+25 -1
View File
@@ -131,7 +131,31 @@ max-locals = 8
max-args = 5
```
## 11. Structural Dependency Mapping (SDM)
### 11. ImGui Defer Patterns
To prevent `PopID` or `End` leaks in immediate-mode rendering, and to keep code flat (0-1 levels of nesting) for AI agents, use the following patterns:
- **The Context Manager Pattern (Mandatory for complex blocks):**
Wrap all `Begin/End` blocks in `imscope` context managers (from `src/imgui_scopes.py`).
```python
with imscope.window("My Window") as (exp, opened):
if exp:
imgui.text("Hello")
with imscope.tab_item("My Tab") as (exp, _):
if exp:
self._render_tab_content()
```
This adds only 1 space of indentation (project standard) and guarantees the corresponding `End` is called even on early returns or exceptions. **Crucial:** Always check the `exp` (expanded/visible) state before rendering content to avoid ID conflicts and performance overhead.
- **The Flat Dispatch Pattern (Recommended for the main loop):**
To avoid nesting multiple window checks, use a dispatch helper that encapsulates the state check and the scope.
```python
self._render_window_if_open("My Window", self._render_my_panel)
```
This keeps the main GUI loop as a flat sequence of declarative calls.
## 12. Structural Dependency Mapping (SDM)
To assist AI agents in evaluating refactoring impact across dynamic codebases, all major definitions SHOULD include terse SDM tags at the end of their docstrings.
+4
View File
@@ -54,6 +54,10 @@ This file tracks all major tracks for the project. Each track has its own detail
*Link: [./tracks/context_comp_presets_20260510/](./tracks/context_comp_presets_20260510/)*
*Goal: Implement Context Preset save/load with validation, and Context Preview before sending to agent.*
12. [ ] **Track: GUI Architecture Refinement & AI-Friendliness**
*Link: [./tracks/gui_architecture_refinement_20260512/](./tracks/gui_architecture_refinement_20260512/)*
*Goal: Reduce nesting and compactness of ImGui code in `gui_2.py`, and formalize ImGui Defer patterns.*
---
## Hot Reload Feature
@@ -0,0 +1,45 @@
# Plan: GUI Architecture Refinement & AI-Friendliness
**Track ID:** gui_architecture_refinement_20260512
**Status:** [~] Draft
## Objective
Reduce nesting and improve compactness of ImGui code in `gui_2.py` to make it more AI-friendly. Formalize the "defer/scope" patterns (inspired by Go's `defer` and Ryan Fleury's macros) in the project style guides to prevent `PopID` / `End` leaks.
## Background & Motivation
The main GUI render loop (`_gui_func__abusrd_try_scope`) has grown to over 600 lines with deep nesting. Raw `imgui.begin()` and `imgui.end()` calls are prone to leaks if an early return occurs or if the return value of `begin` is ignored. While `imscope` context managers solve the leak issue, they still introduce nesting. We need a way to keep the code extremely flat (0-1 levels of nesting) while maintaining safety.
## Proposed Solution
### 1. Update Style Guides (`python.md` & `workflow.md`)
Introduce a new section explicitly defining the "ImGui Defer Patterns":
- **The Context Manager Pattern:** Use `with imscope.window("Name"):` to automatically handle `End()`. This adds only 1 space of indentation (per project rules).
- **The Flat Dispatch Pattern:** To avoid nesting multiple windows, use dispatch helpers like `self._render_window_if_open(name, render_func)` which encapsulate the state-checking, `Begin`, `End`, and execution logic.
### 2. Implement Flat Dispatch Helper
Create a helper method in `App`:
```python
def _render_window_if_open(self, name: str, render_func: Callable[[], None], flag_condition: bool = True) -> None:
if not flag_condition or not self.show_windows.get(name, False): return
with imscope.window(name, self.show_windows[name]) as (exp, opened):
self.show_windows[name] = bool(opened)
if exp: render_func()
```
### 3. Refactor `gui_2.py`
- Extract inline hub definitions (e.g., "Operations Hub", "Discussion Hub", "AI Settings") from `_gui_func__abusrd_try_scope` into dedicated methods (`_render_operations_hub`, etc.).
- Replace the massive `if self.show_windows.get...` blocks in `_gui_func__abusrd_try_scope` with a flat sequence of `_render_window_if_open` calls.
- Rename `_gui_func__abusrd_try_scope` to a cleaner name (e.g., `_gui_func_body`) once stabilized.
## Implementation Steps
1. [x] Edit `conductor/code_styleguides/python.md` to add "ImGui Defer Patterns" under the "AI-Agent Specific Conventions" or "Anti-OOP" section.
2. [x] Edit `conductor/workflow.md` to reference the mandatory use of `imscope` or dispatch helpers for ImGui code.
3. [x] Add `_render_window_if_open` to `gui_2.py`.
4. [x] Extract `_render_operations_hub`, `_render_discussion_hub`, and `_render_ai_settings_hub` in `gui_2.py`.
5. [x] Flatten `_gui_func__abusrd_try_scope` using the new helper.
## Verification & Testing
- Ensure the app launches successfully without `PopID` errors.
- Verify that toggling windows via the menu still opens and closes them correctly.
- Run `uv run pytest tests/test_gui_startup_smoke.py` and `uv run pytest tests/test_gui_window_controls.py`.
+1
View File
@@ -9,6 +9,7 @@
- Use `./scripts/ai_style_formatter.py` for formatting validation
- **NO COMMENTS** unless explicitly requested
- Type hints required for all public functions
- **ImGui Defer Patterns:** Use `imscope` context managers or `_render_window_if_open` dispatch helpers to prevent resource leaks and keep the main loop flat. See `conductor/code_styleguides/python.md` for details.
### CRITICAL: Native Edit Tool Destroys Indentation
+91 -182
View File
@@ -653,6 +653,67 @@ class App:
imgui.pop_style_var(2)
imgui.pop_id()
def _render_window_if_open(self, name: str, render_func: Callable[[], None], flag_condition: bool = True) -> None:
"""Helper to render a window only if its toggle is active."""
if not flag_condition or not self.show_windows.get(name, False): return
with imscope.window(name, self.show_windows[name]) as (exp, opened):
self.show_windows[name] = bool(opened)
if exp: render_func()
def _render_project_settings_hub(self) -> None:
with imscope.tab_bar('context_hub_tabs'):
with imscope.tab_item('Projects'): self._render_projects_panel()
with imscope.tab_item('Paths'): self._render_paths_panel()
def _render_ai_settings_hub(self) -> None:
self._render_persona_selector_panel()
if imgui.collapsing_header("Provider & Model"): self._render_provider_panel()
if imgui.collapsing_header("System Prompts"): self._render_system_prompts_panel()
if imgui.collapsing_header("RAG Settings"): self._render_rag_panel()
self._render_agent_tools_panel()
def _render_discussion_hub(self) -> None:
with imscope.tab_bar("discussion_hub_tabs"):
with imscope.tab_item("Discussion"): self._render_discussion_tab()
with imscope.tab_item("Context Composition"): self._render_context_composition_panel()
with imscope.tab_item("Snapshot"): self._render_snapshot_tab()
with imscope.tab_item("Takes"): self._render_takes_panel()
def _render_operations_hub(self) -> None:
imgui.push_style_var(imgui.StyleVar_.item_spacing, imgui.ImVec2(10, 4))
ch1, self.ui_separate_tool_calls_panel = imgui.checkbox("Pop Out Tool Calls", self.ui_separate_tool_calls_panel)
if ch1: self.show_windows["Tool Calls"] = self.ui_separate_tool_calls_panel
imgui.same_line()
ch2, self.ui_separate_usage_analytics = imgui.checkbox("Pop Out Usage Analytics", self.ui_separate_usage_analytics)
if ch2: self.show_windows["Usage Analytics"] = self.ui_separate_usage_analytics
imgui.same_line()
ch3, self.ui_separate_external_tools = imgui.checkbox('Pop Out External Tools', self.ui_separate_external_tools)
if ch3: self.show_windows['External Tools'] = self.ui_separate_external_tools
imgui.pop_style_var()
show_tc_tab, show_usage_tab = not self.ui_separate_tool_calls_panel, not self.ui_separate_usage_analytics
with imscope.tab_bar("ops_tabs"):
with imscope.tab_item("Comms History"): self._render_comms_history_panel()
if show_tc_tab:
with imscope.tab_item("Tool Calls"): self._render_tool_calls_panel()
if show_usage_tab:
with imscope.tab_item("Usage Analytics"): self._render_usage_analytics_panel()
if not self.ui_separate_external_tools:
with imscope.tab_item("External Tools"):
self._render_external_tools_panel()
imgui.separator(); imgui.text("")
try: self._render_external_editor_panel()
except Exception as e: imgui.text_colored(vec4(1, 0.3, 0.3, 1), f"Error: {str(e)}")
with imscope.tab_item("Workspace Layouts"):
imgui.text("Experimental: Auto-switch layout by Tier")
ch, self.controller.ui_auto_switch_layout = imgui.checkbox("Enable Auto-Switch", self.controller.ui_auto_switch_layout)
if self.controller.ui_auto_switch_layout:
imgui.separator(); imgui.text("Tier Bindings (select profile for each tier)")
profiles = [""] + [p.name for p in self.controller.workspace_profiles.values()]
for t in ["Tier 1", "Tier 2", "Tier 3", "Tier 4"]:
curr = self.controller.ui_tier_layout_bindings.get(t, ""); idx = profiles.index(curr) if curr in profiles else 0
ch_combo, new_idx = imgui.combo(t, idx, profiles)
if ch_combo: self.controller.ui_tier_layout_bindings[t] = profiles[new_idx]
def _show_menus(self) -> None:
"""
[C: tests/test_gui_window_controls.py:test_gui_window_controls_minimize_maximize_close]
@@ -732,16 +793,16 @@ class App:
if hwnd:
btn_w = 40
display_w = imgui.get_io().display_size.x
right_x = display_w - (btn_w * 3)
# Use window width (points) instead of display_size (pixels) for correct scaling
window_w = imgui.get_window_width()
bar_h = imgui.get_window_height()
right_x = window_w - (btn_w * 3)
# Drag area check using an explicit invisible button spanning the empty space
curr_x = imgui.get_cursor_pos_x()
drag_w = right_x - curr_x
if drag_w > 0:
# Use a small positive height to satisfy IM_ASSERT(size_arg.y != 0.0f)
# The menu bar naturally constrains the hit box height anyway.
imgui.invisible_button("##drag_area", (drag_w, 20.0))
imgui.invisible_button("##drag_area", (drag_w, bar_h))
if imgui.is_item_active() and imgui.is_mouse_dragging(0):
# CRITICAL: We must reset ImGui's mouse_down state BEFORE passing control to Windows.
# Otherwise, the Windows modal drag loop swallows the WM_LBUTTONUP event,
@@ -757,17 +818,18 @@ class App:
except Exception:
is_max = False
imgui.set_cursor_pos_x(right_x)
if imgui.button("_", (btn_w, 0)):
# Explicitly set Y to 0 and match button height to bar height for perfect alignment
imgui.set_cursor_pos((right_x, 0))
if imgui.button("_", (btn_w, bar_h)):
win32gui.ShowWindow(hwnd, win32con.SW_MINIMIZE)
imgui.set_cursor_pos_x(right_x + btn_w)
if imgui.button("[=]" if is_max else "[]", (btn_w, 0)):
imgui.set_cursor_pos((right_x + btn_w, 0))
if imgui.button("[=]" if is_max else "[]", (btn_w, bar_h)):
win32gui.ShowWindow(hwnd, win32con.SW_RESTORE if is_max else win32con.SW_MAXIMIZE)
imgui.set_cursor_pos_x(right_x + btn_w * 2)
imgui.set_cursor_pos((right_x + btn_w * 2, 0))
imgui.push_style_color(imgui.Col_.button_hovered, vec4(200, 50, 50, 255))
if imgui.button("X", (btn_w, 0)):
if imgui.button("X", (btn_w, bar_h)):
win32gui.PostMessage(hwnd, win32con.WM_CLOSE, 0, 0)
imgui.pop_style_color()
@@ -985,182 +1047,29 @@ class App:
else: self._tool_log_cache = log_raw
self._tool_log_dirty = False
#region: Project Settings
if self.show_windows.get("Project Settings", False):
with imscope.window("Project Settings", self.show_windows["Project Settings"]) as (exp, opened):
self.show_windows["Project Settings"] = bool(opened)
if exp and imscope.tab_bar('context_hub_tabs'):
if imscope.tab_item('Projects'): self._render_projects_panel()
if imscope.tab_item('Paths'): self._render_paths_panel()
#endregion: Project Settings
self._render_window_if_open("Project Settings", self._render_project_settings_hub)
self._render_window_if_open("Files & Media", self._render_files_and_media)
self._render_window_if_open("AI Settings", self._render_ai_settings_hub)
self._render_window_if_open("Usage Analytics", self._render_usage_analytics_panel, self.ui_separate_usage_analytics)
self._render_window_if_open("MMA Dashboard", self._render_mma_dashboard)
self._render_window_if_open("Task DAG", self._render_task_dag_panel, self.ui_separate_task_dag)
#region: Files & Media window
if self.show_windows.get("Files & Media", False):
exp, opened = imgui.begin("Files & Media", self.show_windows["Files & Media"])
self.show_windows["Files & Media"] = bool(opened)
if opened and exp: self._render_files_and_media()
imgui.end()
#endregion: Files & Media window
#region: AI Settings
if self.show_windows.get("AI Settings", False):
with imscope.window("AI Settings", self.show_windows["AI Settings"]) as (exp, opened):
self.show_windows["AI Settings"] = bool(opened)
if exp:
self._render_persona_selector_panel()
if imgui.collapsing_header("Provider & Model"): self._render_provider_panel()
if imgui.collapsing_header("System Prompts"): self._render_system_prompts_panel()
if imgui.collapsing_header("RAG Settings"): self._render_rag_panel()
self._render_agent_tools_panel()
#endregion: AI Settings
if self.ui_separate_usage_analytics and self.show_windows.get("Usage Analytics", False):
exp, opened = imgui.begin("Usage Analytics", self.show_windows["Usage Analytics"])
self.show_windows["Usage Analytics"] = bool(opened)
if exp:
self._render_usage_analytics_panel()
imgui.end()
if self.show_windows.get("MMA Dashboard", False):
exp, opened = imgui.begin("MMA Dashboard", self.show_windows["MMA Dashboard"])
self.show_windows["MMA Dashboard"] = bool(opened)
if exp:
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_mma_dashboard")
self._render_mma_dashboard()
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_mma_dashboard")
imgui.end()
#region: Seprate Task Dag to Tier 4
if self.ui_separate_task_dag and self.show_windows.get("Task DAG", False):
exp, opened = imgui.begin("Task DAG", self.show_windows["Task DAG"])
self.show_windows["Task DAG"] = bool(opened)
if exp: self._render_task_dag_panel()
imgui.end()
if self.ui_separate_tier1 and self.show_windows.get("Tier 1: Strategy", False):
exp, opened = imgui.begin("Tier 1: Strategy", self.show_windows["Tier 1: Strategy"])
self.show_windows["Tier 1: Strategy"] = bool(opened)
if exp: self._render_tier_stream_panel("Tier 1", "Tier 1")
imgui.end()
if self.ui_separate_tier2 and self.show_windows.get("Tier 2: Tech Lead", False):
exp, opened = imgui.begin("Tier 2: Tech Lead", self.show_windows["Tier 2: Tech Lead"])
self.show_windows["Tier 2: Tech Lead"] = bool(opened)
if exp: self._render_tier_stream_panel("Tier 2", "Tier 2 (Tech Lead)")
imgui.end()
if self.ui_separate_tier3 and self.show_windows.get("Tier 3: Workers", False):
exp, opened = imgui.begin("Tier 3: Workers", self.show_windows["Tier 3: Workers"])
self.show_windows["Tier 3: Workers"] = bool(opened)
if exp: self._render_tier_stream_panel("Tier 3", None)
imgui.end()
if self.ui_separate_tier4 and self.show_windows.get("Tier 4: QA", False):
exp, opened = imgui.begin("Tier 4: QA", self.show_windows["Tier 4: QA"])
self.show_windows["Tier 4: QA"] = bool(opened)
if exp: self._render_tier_stream_panel("Tier 4", "Tier 4 (QA)")
imgui.end()
#endregion: Separate Task Dag to Tier 4
self._render_window_if_open("Tier 1: Strategy", lambda: self._render_tier_stream_panel("Tier 1", "Tier 1"), self.ui_separate_tier1)
self._render_window_if_open("Tier 2: Tech Lead", lambda: self._render_tier_stream_panel("Tier 2", "Tier 2 (Tech Lead)"), self.ui_separate_tier2)
self._render_window_if_open("Tier 3: Workers", lambda: self._render_tier_stream_panel("Tier 3", None), self.ui_separate_tier3)
self._render_window_if_open("Tier 4: QA", lambda: self._render_tier_stream_panel("Tier 4", "Tier 4 (QA)"), self.ui_separate_tier4)
if self.show_windows.get("Theme", False): self._render_theme_panel()
#region: Discussion Hub
if self.show_windows.get("Discussion Hub", False):
with imscope.window("Discussion Hub", self.show_windows["Discussion Hub"]) as (exp, opened):
self.show_windows["Discussion Hub"] = bool(opened)
if exp:
if imscope.tab_bar("discussion_hub_tabs"):
if imscope.tab_item("Discussion"):
self._render_discussion_tab()
if imscope.tab_item("Context Composition"):
self._render_context_composition_panel()
if imscope.tab_item("Snapshot"):
self._render_snapshot_tab()
if imscope.tab_item("Takes"):
self._render_takes_panel()
#endregion: Discussion Hub
self._render_window_if_open("Discussion Hub", self._render_discussion_hub)
self._render_window_if_open("Operations Hub", self._render_operations_hub)
#region: Operations Hub
if self.show_windows.get("Operations Hub", False):
exp, opened = imgui.begin("Operations Hub", self.show_windows["Operations Hub"])
self.show_windows["Operations Hub"] = bool(opened)
if exp:
imgui.push_style_var(imgui.StyleVar_.item_spacing, imgui.ImVec2(10, 4))
ch1, self.ui_separate_tool_calls_panel = imgui.checkbox("Pop Out Tool Calls", self.ui_separate_tool_calls_panel)
if ch1: self.show_windows["Tool Calls"] = self.ui_separate_tool_calls_panel
imgui.same_line()
ch2, self.ui_separate_usage_analytics = imgui.checkbox("Pop Out Usage Analytics", self.ui_separate_usage_analytics)
if ch2: self.show_windows["Usage Analytics"] = self.ui_separate_usage_analytics
imgui.same_line()
ch3, self.ui_separate_external_tools = imgui.checkbox('Pop Out External Tools', self.ui_separate_external_tools)
if ch3: self.show_windows['External Tools'] = self.ui_separate_external_tools
imgui.pop_style_var()
show_tc_tab = not self.ui_separate_tool_calls_panel
show_usage_tab = not self.ui_separate_usage_analytics
with imscope.tab_bar("ops_tabs"):
with imscope.tab_item("Comms History"): self._render_comms_history_panel()
if show_tc_tab:
with imscope.tab_item("Tool Calls"): self._render_tool_calls_panel()
if show_usage_tab:
with imscope.tab_item("Usage Analytics"): self._render_usage_analytics_panel()
if not self.ui_separate_external_tools:
with imscope.tab_item("External Tools"):
self._render_external_tools_panel()
imgui.separator()
imgui.text("")
try:
self._render_external_editor_panel()
except Exception as e:
imgui.text_colored(vec4(1, 0.3, 0.3, 1), f"Error: {str(e)}")
with imscope.tab_item("Workspace Layouts"):
imgui.text("Experimental: Auto-switch layout by Tier")
ch, self.controller.ui_auto_switch_layout = imgui.checkbox("Enable Auto-Switch", self.controller.ui_auto_switch_layout)
if self.controller.ui_auto_switch_layout:
imgui.separator()
imgui.text("Tier Bindings (select profile for each tier)")
profiles = [""] + [p.name for p in self.controller.workspace_profiles.values()]
for t in ["Tier 1", "Tier 2", "Tier 3", "Tier 4"]:
curr = self.controller.ui_tier_layout_bindings.get(t, "")
idx = profiles.index(curr) if curr in profiles else 0
ch_combo, new_idx = imgui.combo(t, idx, profiles)
if ch_combo: self.controller.ui_tier_layout_bindings[t] = profiles[new_idx]
imgui.end()
#endregion: Operations Hub
#region: Separate Message, Response, Tool Calls, External Tools
if self.ui_separate_message_panel and self.show_windows.get("Message", False):
exp, opened = imgui.begin("Message", self.show_windows["Message"])
self.show_windows["Message"] = bool(opened)
if exp: self._render_message_panel()
imgui.end()
if self.ui_separate_response_panel and self.show_windows.get("Response", False):
exp, opened = imgui.begin("Response", self.show_windows["Response"])
self.show_windows["Response"] = bool(opened)
if exp: self._render_response_panel()
imgui.end()
if self.ui_separate_tool_calls_panel and self.show_windows.get("Tool Calls", False):
exp, opened = imgui.begin("Tool Calls", self.show_windows["Tool Calls"])
self.show_windows["Tool Calls"] = bool(opened)
if exp: self._render_tool_calls_panel()
imgui.end()
if self.ui_separate_external_tools and self.show_windows.get('External Tools', False):
exp, opened = imgui.begin('External Tools', self.show_windows['External Tools'])
self.show_windows['External Tools'] = bool(opened)
if exp: self._render_external_tools_panel()
imgui.end()
if self.show_windows.get("Log Management", False):
if self.perf_profiling_enabled: self.perf_monitor.start_component("_render_log_management")
self._render_log_management()
if self.perf_profiling_enabled: self.perf_monitor.end_component("_render_log_management")
#endregion: Separate Message, Response, Tool Calls, External Tools
if self.show_windows.get("Diagnostics", False): self._render_diagnostics_panel()
self._render_window_if_open("Message", self._render_message_panel, self.ui_separate_message_panel)
self._render_window_if_open("Response", self._render_response_panel, self.ui_separate_response_panel)
self._render_window_if_open("Tool Calls", self._render_tool_calls_panel, self.ui_separate_tool_calls_panel)
self._render_window_if_open("External Tools", self._render_external_tools_panel, self.ui_separate_external_tools)
self._render_window_if_open("Log Management", self._render_log_management)
self._render_window_if_open("Diagnostics", self._render_diagnostics_panel)
self.perf_monitor.end_frame()
+27 -9
View File
@@ -24,7 +24,8 @@ class _ScopeChild:
self._sy = size_y
self._flags = flags
def __enter__(self):
return imgui.begin_child(self._id, self._sx, self._sy, self._flags)
res = imgui.begin_child(self._id, self._sx, self._sy, self._flags)
return res
def __exit__(self, *args):
imgui.end_child()
return False
@@ -35,17 +36,24 @@ class _ScopeTable:
self._name = name
self._columns = columns
self._flags = flags
self._active = False
def __enter__(self):
return imgui.begin_table(self._name, self._columns, self._flags)
self._active = imgui.begin_table(self._name, self._columns, self._flags)
return self._active
def __exit__(self, *args):
if self._active:
imgui.end_table()
return False
def menu_bar(): return _ScopeMenuBar()
class _ScopeMenuBar:
def __init__(self):
self._active = False
def __enter__(self):
return imgui.begin_menu_bar()
self._active = imgui.begin_menu_bar()
return self._active
def __exit__(self, *args):
if self._active:
imgui.end_menu_bar()
return False
@@ -53,9 +61,12 @@ def menu(label: str): return _ScopeMenu(label)
class _ScopeMenu:
def __init__(self, label: str):
self._label = label
self._active = False
def __enter__(self):
return imgui.begin_menu(self._label)
self._active = imgui.begin_menu(self._label)
return self._active
def __exit__(self, *args):
if self._active:
imgui.end_menu()
return False
@@ -63,9 +74,12 @@ def popup(id_str: str): return _ScopePopup(id_str)
class _ScopePopup:
def __init__(self, id_str: str):
self._id = id_str
self._active = False
def __enter__(self):
return imgui.begin_popup(self._id)
self._active = imgui.begin_popup(self._id)
return self._active
def __exit__(self, *args):
if self._active:
imgui.end_popup()
return False
@@ -120,9 +134,12 @@ class _ScopeTabBar:
def __init__(self, id_str: str, flags: int):
self._id = id_str
self._flags = flags
self._active = False
def __enter__(self):
return imgui.begin_tab_bar(self._id, self._flags)
self._active = imgui.begin_tab_bar(self._id, self._flags)
return self._active
def __exit__(self, *args):
if self._active:
imgui.end_tab_bar()
return False
@@ -131,11 +148,12 @@ class _ScopeTabItem:
def __init__(self, label: str, flags: int):
self._label = label
self._flags = flags
self._expanded = False
self._open = None
def __enter__(self):
exp, self._open = imgui.begin_tab_item(self._label, None, self._flags)
return exp, self._open
self._expanded, self._open = imgui.begin_tab_item(self._label, None, self._flags)
return self._expanded, self._open
def __exit__(self, *args):
if self._open:
if self._expanded:
imgui.end_tab_item()
return False