diff --git a/python/taichi/ui/imgui.py b/python/taichi/ui/imgui.py index 570a815526001..13a0acf266f7f 100644 --- a/python/taichi/ui/imgui.py +++ b/python/taichi/ui/imgui.py @@ -75,40 +75,109 @@ def checkbox(self, text, old_value): return self.gui.checkbox(text, old_value) def slider_int(self, text, old_value, minimum, maximum): - """Declares a slider, and returns its newest value. + """Declares an integer slider, and returns its newest value. Args: - text (str): a line of text to be shown next to the slider - old_value (int) : the current value of the slider. + text (str): a line of text to be shown next to the slider. + old_value (int or tuple): the current value. Can be a scalar int + or a tuple of 2-4 ints for multi-component sliders. minimum (int): the minimum value of the slider. maximum (int): the maximum value of the slider. Returns: - int: the updated value of the slider. + int or tuple: the updated value (same type as input). """ + if isinstance(old_value, (tuple, list)): + n = len(old_value) + if n == 2: + return self.gui.slider_int2(text, tuple(old_value), minimum, maximum) + if n == 3: + return self.gui.slider_int3(text, tuple(old_value), minimum, maximum) + if n == 4: + return self.gui.slider_int4(text, tuple(old_value), minimum, maximum) + raise ValueError(f"slider_int expects 1-4 components, got {n}") return self.gui.slider_int(text, old_value, minimum, maximum) def slider_float(self, text, old_value, minimum, maximum): - """Declares a slider, and returns its newest value. + """Declares a float slider, and returns its newest value. Args: - text (str): a line of text to be shown next to the slider - old_value (float): the current value of the slider. + text (str): a line of text to be shown next to the slider. + old_value (float or tuple): the current value. Can be a scalar float + or a tuple of 2-4 floats for multi-component sliders. minimum (float): the minimum value of the slider. maximum (float): the maximum value of the slider. + + Returns: + float or tuple: the updated value (same type as input). """ + if isinstance(old_value, (tuple, list)): + n = len(old_value) + if n == 2: + return self.gui.slider_float2(text, tuple(old_value), minimum, maximum) + if n == 3: + return self.gui.slider_float3(text, tuple(old_value), minimum, maximum) + if n == 4: + return self.gui.slider_float4(text, tuple(old_value), minimum, maximum) + raise ValueError(f"slider_float expects 1-4 components, got {n}") return self.gui.slider_float(text, old_value, minimum, maximum) + def color_edit(self, text, old_value): + """Declares a color edit palette. + + Auto-detects RGB vs RGBA based on tuple size. + + Args: + text (str): a line of text to be shown next to the palette. + old_value (tuple): the current color value. Can be a tuple of + 3 floats (RGB) or 4 floats (RGBA) in [0,1]. + + Returns: + tuple: the updated color (same size as input). + """ + n = len(old_value) + if n == 3: + return self.gui.color_edit_3(text, tuple(old_value)) + if n == 4: + return self.gui.color_edit_4(text, tuple(old_value)) + raise ValueError(f"color_edit expects 3 (RGB) or 4 (RGBA) components, got {n}") + def color_edit_3(self, text, old_value): - """Declares a color edit palate. + """Declares an RGB color edit palette. + + Note: + Prefer using :meth:`color_edit` which auto-detects RGB/RGBA. Args: - text (str): a line of text to be shown next to the palate. + text (str): a line of text to be shown next to the palette. old_value (Tuple[float]): the current value of the color, this \ - should be a tuple of floats in [0,1] that indicates RGB values. + should be a tuple of 3 floats in [0,1] that indicates RGB values. + + Returns: + tuple: the updated RGB color as (r, g, b). """ return self.gui.color_edit_3(text, old_value) + def color_picker(self, text, old_value): + """Declares a full color picker widget with color wheel/square. + + Auto-detects RGB vs RGBA based on tuple size. + + Args: + text (str): a line of text to be shown next to the picker. + old_value (tuple): the current color value. Can be a tuple of + 3 floats (RGB) or 4 floats (RGBA) in [0,1]. + + Returns: + tuple: the updated color (same size as input). + """ + n = len(old_value) + if n == 3: + return self.gui.color_picker_3(text, tuple(old_value)) + if n == 4: + return self.gui.color_picker_4(text, tuple(old_value)) + raise ValueError(f"color_picker expects 3 (RGB) or 4 (RGBA) components, got {n}") + def button(self, text): """Declares a button, and returns whether or not it had just been clicked. @@ -116,3 +185,450 @@ def button(self, text): text (str): a line of text to be shown next to the button. """ return self.gui.button(text) + + def input_int(self, label, old_value): + """Integer input field with +/- buttons. + + Args: + label (str): Label for the input field. + old_value (int or tuple): Current value. Can be a scalar int + or a tuple of 2-4 ints for multi-component inputs. + + Returns: + int or tuple: The updated value (same type as input). + """ + if isinstance(old_value, (tuple, list)): + n = len(old_value) + if n == 2: + return self.gui.input_int2(label, tuple(old_value)) + if n == 3: + return self.gui.input_int3(label, tuple(old_value)) + if n == 4: + return self.gui.input_int4(label, tuple(old_value)) + raise ValueError(f"input_int expects 1-4 components, got {n}") + return self.gui.input_int(label, old_value) + + def input_float(self, label, old_value): + """Float input field with +/- buttons. + + Args: + label (str): Label for the input field. + old_value (float or tuple): Current value. Can be a scalar float + or a tuple of 2-4 floats for multi-component inputs. + + Returns: + float or tuple: The updated value (same type as input). + """ + if isinstance(old_value, (tuple, list)): + n = len(old_value) + if n == 2: + return self.gui.input_float2(label, tuple(old_value)) + if n == 3: + return self.gui.input_float3(label, tuple(old_value)) + if n == 4: + return self.gui.input_float4(label, tuple(old_value)) + raise ValueError(f"input_float expects 1-4 components, got {n}") + return self.gui.input_float(label, old_value) + + def drag_int(self, label, old_value, speed=1.0, minimum=0, maximum=0): + """Draggable integer input. + + Args: + label (str): Label for the input. + old_value (int or tuple): Current value. Can be a scalar int + or a tuple of 2-4 ints for multi-component inputs. + speed (float): Drag speed multiplier. + minimum (int): Minimum value (0 for no limit). + maximum (int): Maximum value (0 for no limit). + + Returns: + int or tuple: The updated value (same type as input). + """ + if isinstance(old_value, (tuple, list)): + n = len(old_value) + if n == 2: + return self.gui.drag_int2(label, tuple(old_value), speed, minimum, maximum) + if n == 3: + return self.gui.drag_int3(label, tuple(old_value), speed, minimum, maximum) + if n == 4: + return self.gui.drag_int4(label, tuple(old_value), speed, minimum, maximum) + raise ValueError(f"drag_int expects 1-4 components, got {n}") + return self.gui.drag_int(label, old_value, speed, minimum, maximum) + + def drag_float(self, label, old_value, speed=1.0, minimum=0.0, maximum=0.0): + """Draggable float input. + + Args: + label (str): Label for the input. + old_value (float or tuple): Current value. Can be a scalar float + or a tuple of 2-4 floats for multi-component inputs. + speed (float): Drag speed multiplier. + minimum (float): Minimum value (0.0 for no limit). + maximum (float): Maximum value (0.0 for no limit). + + Returns: + float or tuple: The updated value (same type as input). + """ + if isinstance(old_value, (tuple, list)): + n = len(old_value) + if n == 2: + return self.gui.drag_float2(label, tuple(old_value), speed, minimum, maximum) + if n == 3: + return self.gui.drag_float3(label, tuple(old_value), speed, minimum, maximum) + if n == 4: + return self.gui.drag_float4(label, tuple(old_value), speed, minimum, maximum) + raise ValueError(f"drag_float expects 1-4 components, got {n}") + return self.gui.drag_float(label, old_value, speed, minimum, maximum) + + def combo(self, label, current_index, items): + """Combo box (dropdown) for selecting from a tuple of items. + + Args: + label (str): Label for the combo box. + current_index (int): Currently selected index (0-based). + items (tuple[str, ...]): Tuple of string options. + + Returns: + int: The newly selected index. + + Note: + Converting Python strings to ImGui format is cached when the same + tuple object is passed across frames. For best performance, define + options at module or class level (not inside the render loop):: + + DIFFICULTIES = ("Easy", "Medium", "Hard") # module level + + while window.running: + with gui.sub_window(...) as w: + # Cached - same tuple object each frame + idx = w.combo("Difficulty", idx, DIFFICULTIES) + window.show() + """ + return self.gui.combo(label, current_index, items) + + def tree_node_push(self, label): + """Begin a collapsible tree node (low-level). + + Args: + label (str): Label for the tree node. + + Returns: + bool: True if the node is expanded, False if collapsed. + + Note: + You must call tree_node_pop() if this returns True. + Consider using tree_node() generator instead for automatic cleanup. + """ + return self.gui.tree_node_push(label) + + def tree_node_pop(self): + """End a tree node (low-level). + + Only call this if tree_node_push() returned True. + """ + self.gui.tree_node_pop() + + def tree_node(self, label): + """Collapsible tree node (generator). + + Use with a for loop - the body runs only if the node is expanded, + and tree_node_pop is called automatically. + + Args: + label (str): Label for the tree node. + + Yields: + Nothing, but loop body executes only if node is expanded. + + Example:: + + for _ in gui.tree_node("Settings"): + gui.slider_float("Volume", volume, 0, 1) + for _ in gui.tree_node("Advanced"): + gui.checkbox("Debug", debug) + """ + if self.gui.tree_node_push(label): + try: + yield + finally: + self.gui.tree_node_pop() + + def separator(self): + """Draw a horizontal separator line.""" + self.gui.separator() + + def same_line(self): + """Place the next widget on the same line as the previous one.""" + self.gui.same_line() + + @contextmanager + def indent(self): + """Indent subsequent widgets (context manager). + + Example:: + + gui.text("Parent") + with gui.indent(): + gui.text("Child 1") + gui.text("Child 2") + gui.text("Back to normal") + """ + self.gui.indent() + try: + yield + finally: + self.gui.unindent() + + def progress_bar(self, fraction): + """Display a progress bar. + + Args: + fraction (float): Progress value between 0.0 and 1.0. + """ + self.gui.progress_bar(fraction) + + def collapsing_header(self, label): + """A collapsible header that reveals content when expanded. + + Args: + label (str): Label for the header. + + Returns: + bool: True if the header is expanded, False if collapsed. + + Example:: + + if gui.collapsing_header("Advanced Settings"): + gui.slider_float("Detail", detail, 0, 1) + """ + return self.gui.collapsing_header(label) + + def selectable(self, label, selected=False): + """A selectable item (like a list entry). + + Args: + label (str): Label for the item. + selected (bool): Whether the item is currently selected. + + Returns: + bool: The new selected state (toggled if clicked). + """ + return self.gui.selectable(label, selected) + + def radio_button(self, label, active): + """A radio button. + + Args: + label (str): Label for the button. + active (bool): Whether this option is currently selected. + + Returns: + bool: True if clicked (use to update selection state). + + Example:: + + if gui.radio_button("Option A", choice == 0): + choice = 0 + if gui.radio_button("Option B", choice == 1): + choice = 1 + """ + return self.gui.radio_button(label, active) + + def listbox(self, label, current_index, items, height_in_items=-1): + """A listbox for selecting from a tuple of items. + + Args: + label (str): Label for the listbox. + current_index (int): Currently selected index (0-based). + items (tuple[str, ...]): Tuple of string options. + height_in_items (int): Number of items to show (-1 for default). + + Returns: + int: The newly selected index. + + Note: + Like combo(), uses caching for tuple→string conversion. + Define items as module-level constants for best performance. + """ + return self.gui.listbox(label, current_index, items, height_in_items) + + def begin_tab_bar(self, bar_id): + """Begin a tab bar (low-level). + + Args: + bar_id (str): Unique identifier for the tab bar. + + Returns: + bool: True if the tab bar is visible. + + Note: + Must call end_tab_bar() if this returns True. + Consider using tab_bar() generator instead. + """ + return self.gui.begin_tab_bar(bar_id) + + def end_tab_bar(self): + """End a tab bar (low-level). + + Only call this if begin_tab_bar() returned True. + """ + self.gui.end_tab_bar() + + def begin_tab_item(self, label): + """Begin a tab item (low-level). + + Args: + label (str): Label for the tab. + + Returns: + bool: True if this tab is selected. + + Note: + Must call end_tab_item() if this returns True. + Consider using tab_item() generator instead. + """ + return self.gui.begin_tab_item(label) + + def end_tab_item(self): + """End a tab item (low-level). + + Only call this if begin_tab_item() returned True. + """ + self.gui.end_tab_item() + + def tab_bar(self, bar_id): + """Tab bar container (generator). + + Use with a for loop - the body runs only if the tab bar is visible, + and end_tab_bar is called automatically. + + Args: + bar_id (str): Unique identifier for the tab bar. + + Yields: + Nothing, but loop body executes only if visible. + + Example:: + + for _ in gui.tab_bar("my_tabs"): + for _ in gui.tab_item("Tab 1"): + gui.text("Content for tab 1") + for _ in gui.tab_item("Tab 2"): + gui.text("Content for tab 2") + """ + if self.gui.begin_tab_bar(bar_id): + try: + yield + finally: + self.gui.end_tab_bar() + + def tab_item(self, label): + """Tab item (generator). + + Use with a for loop inside tab_bar - the body runs only if this tab + is selected, and end_tab_item is called automatically. + + Args: + label (str): Label for the tab. + + Yields: + Nothing, but loop body executes only if tab is selected. + + Example:: + + for _ in gui.tab_bar("settings"): + for _ in gui.tab_item("General"): + gui.checkbox("Enable feature", enabled) + for _ in gui.tab_item("Advanced"): + gui.slider_int("Level", level, 1, 10) + """ + if self.gui.begin_tab_item(label): + try: + yield + finally: + self.gui.end_tab_item() + + def begin_table(self, table_id, columns): + """Begin a table (low-level). + + Args: + table_id (str): Unique identifier for the table. + columns (int): Number of columns. + + Returns: + bool: True if the table is visible and should be rendered. + + Note: + Must call end_table() if this returns True. + Consider using table() generator instead for automatic cleanup. + """ + return self.gui.begin_table(table_id, columns) + + def end_table(self): + """End a table (low-level). + + Only call this if begin_table() returned True. + """ + self.gui.end_table() + + def table_setup_column(self, label): + """Set up a column header for the table. + + Call after begin_table() and before the first row. + + Args: + label (str): Label for the column header. + """ + self.gui.table_setup_column(label) + + def table_headers_row(self): + """Submit the header row after setting up columns. + + Call after all table_setup_column() calls and before data rows. + """ + self.gui.table_headers_row() + + def table_next_row(self): + """Begin a new row in the table.""" + self.gui.table_next_row() + + def table_next_column(self): + """Move to the next column in the current row. + + Returns: + bool: True if the column is visible (not clipped). + """ + return self.gui.table_next_column() + + def table(self, table_id, columns): + """Table container (generator). + + Use with a for loop - the body runs only if the table is visible, + and end_table is called automatically. + + Args: + table_id (str): Unique identifier for the table. + columns (int): Number of columns. + + Yields: + Nothing, but loop body executes only if table is visible. + + Example:: + + for _ in gui.table("my_table", 3): + gui.table_setup_column("ID") + gui.table_setup_column("Name") + gui.table_setup_column("Price") + gui.table_headers_row() + + for item in items: + gui.table_next_row() + gui.table_next_column(); gui.text(str(item["id"])) + gui.table_next_column(); gui.text(item["name"]) + gui.table_next_column(); gui.text(f"${item['price']:.2f}") + """ + if self.gui.begin_table(table_id, columns): + try: + yield + finally: + self.gui.end_table() diff --git a/taichi/python/export_ggui.cpp b/taichi/python/export_ggui.cpp index 965c54de70b01..0a51ced19bef7 100644 --- a/taichi/python/export_ggui.cpp +++ b/taichi/python/export_ggui.cpp @@ -1,4 +1,5 @@ +#include #include #include "pybind11/pybind11.h" #include @@ -35,14 +36,56 @@ namespace taichi::ui { using namespace taichi::lang; +glm::vec2 tuple_to_vec2(pybind11::tuple t) { + return glm::vec2(t[0].cast(), t[1].cast()); +} + glm::vec3 tuple_to_vec3(pybind11::tuple t) { return glm::vec3(t[0].cast(), t[1].cast(), t[2].cast()); } +glm::vec4 tuple_to_vec4(pybind11::tuple t) { + return glm::vec4(t[0].cast(), t[1].cast(), t[2].cast(), + t[3].cast()); +} + +glm::ivec2 tuple_to_ivec2(pybind11::tuple t) { + return glm::ivec2(t[0].cast(), t[1].cast()); +} + +glm::ivec3 tuple_to_ivec3(pybind11::tuple t) { + return glm::ivec3(t[0].cast(), t[1].cast(), t[2].cast()); +} + +glm::ivec4 tuple_to_ivec4(pybind11::tuple t) { + return glm::ivec4(t[0].cast(), t[1].cast(), t[2].cast(), + t[3].cast()); +} + +pybind11::tuple vec2_to_tuple(glm::vec2 v) { + return pybind11::make_tuple(v.x, v.y); +} + pybind11::tuple vec3_to_tuple(glm::vec3 v) { return pybind11::make_tuple(v.x, v.y, v.z); } +pybind11::tuple vec4_to_tuple(glm::vec4 v) { + return pybind11::make_tuple(v.x, v.y, v.z, v.w); +} + +pybind11::tuple ivec2_to_tuple(glm::ivec2 v) { + return pybind11::make_tuple(v.x, v.y); +} + +pybind11::tuple ivec3_to_tuple(glm::ivec3 v) { + return pybind11::make_tuple(v.x, v.y, v.z); +} + +pybind11::tuple ivec4_to_tuple(glm::ivec4 v) { + return pybind11::make_tuple(v.x, v.y, v.z, v.w); +} + // Here we convert the 2d-array to numpy array using pybind. Refs: // https://pybind11.readthedocs.io/en/stable/advanced/pycpp/numpy.html?highlight=array_t#vectorizing-functions // https://stackoverflow.com/questions/44659924/returning-numpy-arrays-via-pybind11 @@ -59,7 +102,40 @@ py::array_t mat4_to_nparray(glm::mat4 mat) { } struct PyGui { - GuiBase *gui; // not owned + GuiBase *gui = nullptr; // not owned + + // Cache for string list items (combo, listbox): label -> cached data + // Frame-based cleanup removes entries not used since last frame + struct StringListCache { + py::tuple items_tuple; // for identity comparison + std::vector items_str; // owns the string data + std::vector items_cstr; // points into items_str + bool touched = false; // used this frame? + }; + std::unordered_map string_list_cache_; + + // Get cached C string pointers for a tuple of Python strings. + // Rebuilds cache if tuple identity changed; marks entry as touched. + const std::vector &get_cached_strings_(const std::string &label, + py::tuple items_py) { + auto it = string_list_cache_.find(label); + if (it == string_list_cache_.end() || + !it->second.items_tuple.is(items_py)) { + StringListCache cache; + cache.items_tuple = items_py; + for (auto item : items_py) { + cache.items_str.push_back(item.cast()); + } + for (const auto &s : cache.items_str) { + cache.items_cstr.push_back(s.c_str()); + } + string_list_cache_[label] = std::move(cache); + it = string_list_cache_.find(label); + } + it->second.touched = true; + return it->second.items_cstr; + } + void begin(std::string name, float x, float y, float width, float height) { gui->begin(name, x, y, width, height); } @@ -78,20 +154,248 @@ struct PyGui { int slider_int(std::string name, int old_value, int minimum, int maximum) { return gui->slider_int(name, old_value, minimum, maximum); } + py::tuple slider_int2(std::string name, + py::tuple old_value, + int minimum, + int maximum) { + return ivec2_to_tuple( + gui->slider_int2(name, tuple_to_ivec2(old_value), minimum, maximum)); + } + py::tuple slider_int3(std::string name, + py::tuple old_value, + int minimum, + int maximum) { + return ivec3_to_tuple( + gui->slider_int3(name, tuple_to_ivec3(old_value), minimum, maximum)); + } + py::tuple slider_int4(std::string name, + py::tuple old_value, + int minimum, + int maximum) { + return ivec4_to_tuple( + gui->slider_int4(name, tuple_to_ivec4(old_value), minimum, maximum)); + } float slider_float(std::string name, float old_value, float minimum, float maximum) { return gui->slider_float(name, old_value, minimum, maximum); } + py::tuple slider_float2(std::string name, + py::tuple old_value, + float minimum, + float maximum) { + return vec2_to_tuple( + gui->slider_float2(name, tuple_to_vec2(old_value), minimum, maximum)); + } + py::tuple slider_float3(std::string name, + py::tuple old_value, + float minimum, + float maximum) { + return vec3_to_tuple( + gui->slider_float3(name, tuple_to_vec3(old_value), minimum, maximum)); + } + py::tuple slider_float4(std::string name, + py::tuple old_value, + float minimum, + float maximum) { + return vec4_to_tuple( + gui->slider_float4(name, tuple_to_vec4(old_value), minimum, maximum)); + } py::tuple color_edit_3(std::string name, py::tuple old_value) { glm::vec3 old_color = tuple_to_vec3(old_value); glm::vec3 new_color = gui->color_edit_3(name, old_color); return vec3_to_tuple(new_color); } + py::tuple color_edit_4(std::string name, py::tuple old_value) { + glm::vec4 old_color = tuple_to_vec4(old_value); + glm::vec4 new_color = gui->color_edit_4(name, old_color); + return vec4_to_tuple(new_color); + } + py::tuple color_picker_3(std::string name, py::tuple old_value) { + glm::vec3 old_color = tuple_to_vec3(old_value); + glm::vec3 new_color = gui->color_picker_3(name, old_color); + return vec3_to_tuple(new_color); + } + py::tuple color_picker_4(std::string name, py::tuple old_value) { + glm::vec4 old_color = tuple_to_vec4(old_value); + glm::vec4 new_color = gui->color_picker_4(name, old_color); + return vec4_to_tuple(new_color); + } bool button(std::string name) { return gui->button(name); } + int input_int(std::string label, int old_value) { + return gui->input_int(label, old_value); + } + py::tuple input_int2(std::string label, py::tuple old_value) { + return ivec2_to_tuple(gui->input_int2(label, tuple_to_ivec2(old_value))); + } + py::tuple input_int3(std::string label, py::tuple old_value) { + return ivec3_to_tuple(gui->input_int3(label, tuple_to_ivec3(old_value))); + } + py::tuple input_int4(std::string label, py::tuple old_value) { + return ivec4_to_tuple(gui->input_int4(label, tuple_to_ivec4(old_value))); + } + float input_float(std::string label, float old_value) { + return gui->input_float(label, old_value); + } + py::tuple input_float2(std::string label, py::tuple old_value) { + return vec2_to_tuple(gui->input_float2(label, tuple_to_vec2(old_value))); + } + py::tuple input_float3(std::string label, py::tuple old_value) { + return vec3_to_tuple(gui->input_float3(label, tuple_to_vec3(old_value))); + } + py::tuple input_float4(std::string label, py::tuple old_value) { + return vec4_to_tuple(gui->input_float4(label, tuple_to_vec4(old_value))); + } + int drag_int(std::string label, + int old_value, + float speed, + int minimum, + int maximum) { + return gui->drag_int(label, old_value, speed, minimum, maximum); + } + py::tuple drag_int2(std::string label, + py::tuple old_value, + float speed, + int minimum, + int maximum) { + return ivec2_to_tuple(gui->drag_int2(label, tuple_to_ivec2(old_value), + speed, minimum, maximum)); + } + py::tuple drag_int3(std::string label, + py::tuple old_value, + float speed, + int minimum, + int maximum) { + return ivec3_to_tuple(gui->drag_int3(label, tuple_to_ivec3(old_value), + speed, minimum, maximum)); + } + py::tuple drag_int4(std::string label, + py::tuple old_value, + float speed, + int minimum, + int maximum) { + return ivec4_to_tuple(gui->drag_int4(label, tuple_to_ivec4(old_value), + speed, minimum, maximum)); + } + float drag_float(std::string label, + float old_value, + float speed, + float minimum, + float maximum) { + return gui->drag_float(label, old_value, speed, minimum, maximum); + } + py::tuple drag_float2(std::string label, + py::tuple old_value, + float speed, + float minimum, + float maximum) { + return vec2_to_tuple(gui->drag_float2(label, tuple_to_vec2(old_value), + speed, minimum, maximum)); + } + py::tuple drag_float3(std::string label, + py::tuple old_value, + float speed, + float minimum, + float maximum) { + return vec3_to_tuple(gui->drag_float3(label, tuple_to_vec3(old_value), + speed, minimum, maximum)); + } + py::tuple drag_float4(std::string label, + py::tuple old_value, + float speed, + float minimum, + float maximum) { + return vec4_to_tuple(gui->drag_float4(label, tuple_to_vec4(old_value), + speed, minimum, maximum)); + } + bool tree_node_push(std::string label) { + return gui->tree_node_push(label); + } + void tree_node_pop() { + gui->tree_node_pop(); + } + void separator() { + gui->separator(); + } + void same_line() { + gui->same_line(); + } + void indent() { + gui->indent(); + } + void unindent() { + gui->unindent(); + } + void progress_bar(float fraction) { + gui->progress_bar(fraction); + } + bool collapsing_header(std::string label) { + return gui->collapsing_header(label); + } + bool selectable(std::string label, bool selected) { + return gui->selectable(label, selected); + } + bool radio_button(std::string label, bool active) { + return gui->radio_button(label, active); + } + bool begin_tab_bar(std::string id) { + return gui->begin_tab_bar(id); + } + void end_tab_bar() { + gui->end_tab_bar(); + } + bool begin_tab_item(std::string label) { + return gui->begin_tab_item(label); + } + void end_tab_item() { + gui->end_tab_item(); + } + bool begin_table(std::string id, int columns) { + return gui->begin_table(id, columns); + } + void end_table() { + gui->end_table(); + } + void table_setup_column(std::string label) { + gui->table_setup_column(label); + } + void table_headers_row() { + gui->table_headers_row(); + } + void table_next_row() { + gui->table_next_row(); + } + bool table_next_column() { + return gui->table_next_column(); + } + int combo(std::string label, int current_item, py::tuple items_py) { + const auto &items = get_cached_strings_(label, items_py); + return gui->combo(label, current_item, items); + } + + int listbox(std::string label, + int current_item, + py::tuple items_py, + int height_in_items) { + const auto &items = get_cached_strings_(label, items_py); + return gui->listbox(label, current_item, items, height_in_items); + } + + // Called at frame end to clean up stale cache entries + void frame_end() { + for (auto it = string_list_cache_.begin(); + it != string_list_cache_.end();) { + if (!it->second.touched) { + it = string_list_cache_.erase(it); + } else { + it->second.touched = false; + ++it; + } + } + } }; struct PyCamera { @@ -511,6 +815,7 @@ struct PyCanvas { struct PyWindow { std::unique_ptr window{nullptr}; + std::unique_ptr py_gui_{nullptr}; PyWindow(Program *prog, std::string name, @@ -596,6 +901,9 @@ struct PyWindow { void show() { window->show(); + if (py_gui_) { + py_gui_->frame_end(); + } } bool is_pressed(std::string button) { @@ -635,9 +943,12 @@ struct PyWindow { return scene; } - PyGui gui() { - PyGui gui = {window->gui()}; - return gui; + PyGui &gui() { + if (!py_gui_) { + py_gui_ = std::make_unique(); + py_gui_->gui = window->gui(); + } + return *py_gui_; } // this is so that the GUI class does not need to use any pybind related stuff @@ -697,9 +1008,56 @@ void export_ggui(py::module &m) { .def("text_colored", &PyGui::text_colored) .def("checkbox", &PyGui::checkbox) .def("slider_int", &PyGui::slider_int) + .def("slider_int2", &PyGui::slider_int2) + .def("slider_int3", &PyGui::slider_int3) + .def("slider_int4", &PyGui::slider_int4) .def("slider_float", &PyGui::slider_float) + .def("slider_float2", &PyGui::slider_float2) + .def("slider_float3", &PyGui::slider_float3) + .def("slider_float4", &PyGui::slider_float4) .def("color_edit_3", &PyGui::color_edit_3) - .def("button", &PyGui::button); + .def("color_edit_4", &PyGui::color_edit_4) + .def("color_picker_3", &PyGui::color_picker_3) + .def("color_picker_4", &PyGui::color_picker_4) + .def("button", &PyGui::button) + .def("input_int", &PyGui::input_int) + .def("input_int2", &PyGui::input_int2) + .def("input_int3", &PyGui::input_int3) + .def("input_int4", &PyGui::input_int4) + .def("input_float", &PyGui::input_float) + .def("input_float2", &PyGui::input_float2) + .def("input_float3", &PyGui::input_float3) + .def("input_float4", &PyGui::input_float4) + .def("drag_int", &PyGui::drag_int) + .def("drag_int2", &PyGui::drag_int2) + .def("drag_int3", &PyGui::drag_int3) + .def("drag_int4", &PyGui::drag_int4) + .def("drag_float", &PyGui::drag_float) + .def("drag_float2", &PyGui::drag_float2) + .def("drag_float3", &PyGui::drag_float3) + .def("drag_float4", &PyGui::drag_float4) + .def("tree_node_push", &PyGui::tree_node_push) + .def("tree_node_pop", &PyGui::tree_node_pop) + .def("separator", &PyGui::separator) + .def("same_line", &PyGui::same_line) + .def("indent", &PyGui::indent) + .def("unindent", &PyGui::unindent) + .def("progress_bar", &PyGui::progress_bar) + .def("combo", &PyGui::combo) + .def("collapsing_header", &PyGui::collapsing_header) + .def("selectable", &PyGui::selectable) + .def("radio_button", &PyGui::radio_button) + .def("listbox", &PyGui::listbox) + .def("begin_tab_bar", &PyGui::begin_tab_bar) + .def("end_tab_bar", &PyGui::end_tab_bar) + .def("begin_tab_item", &PyGui::begin_tab_item) + .def("end_tab_item", &PyGui::end_tab_item) + .def("begin_table", &PyGui::begin_table) + .def("end_table", &PyGui::end_table) + .def("table_setup_column", &PyGui::table_setup_column) + .def("table_headers_row", &PyGui::table_headers_row) + .def("table_next_row", &PyGui::table_next_row) + .def("table_next_column", &PyGui::table_next_column); py::class_(m, "PyScene") .def(py::init<>()) diff --git a/taichi/ui/common/gui_base.h b/taichi/ui/common/gui_base.h index a7d296cbe7b06..f98144159aab1 100644 --- a/taichi/ui/common/gui_base.h +++ b/taichi/ui/common/gui_base.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include "taichi/ui/utils/utils.h" namespace taichi::ui { @@ -19,13 +20,124 @@ class GuiBase { int old_value, int minimum, int maximum) = 0; + virtual glm::ivec2 slider_int2(const std::string &name, + glm::ivec2 old_value, + int minimum, + int maximum) = 0; + virtual glm::ivec3 slider_int3(const std::string &name, + glm::ivec3 old_value, + int minimum, + int maximum) = 0; + virtual glm::ivec4 slider_int4(const std::string &name, + glm::ivec4 old_value, + int minimum, + int maximum) = 0; virtual float slider_float(const std::string &name, float old_value, float minimum, float maximum) = 0; + virtual glm::vec2 slider_float2(const std::string &name, + glm::vec2 old_value, + float minimum, + float maximum) = 0; + virtual glm::vec3 slider_float3(const std::string &name, + glm::vec3 old_value, + float minimum, + float maximum) = 0; + virtual glm::vec4 slider_float4(const std::string &name, + glm::vec4 old_value, + float minimum, + float maximum) = 0; virtual glm::vec3 color_edit_3(const std::string &name, glm::vec3 old_value) = 0; + virtual glm::vec4 color_edit_4(const std::string &name, + glm::vec4 old_value) = 0; + virtual glm::vec3 color_picker_3(const std::string &name, + glm::vec3 old_value) = 0; + virtual glm::vec4 color_picker_4(const std::string &name, + glm::vec4 old_value) = 0; virtual bool button(const std::string &text) = 0; + virtual int combo(const std::string &label, + int current_item, + const std::vector &items) = 0; + virtual int input_int(const std::string &label, int old_value) = 0; + virtual glm::ivec2 input_int2(const std::string &label, + glm::ivec2 old_value) = 0; + virtual glm::ivec3 input_int3(const std::string &label, + glm::ivec3 old_value) = 0; + virtual glm::ivec4 input_int4(const std::string &label, + glm::ivec4 old_value) = 0; + virtual float input_float(const std::string &label, float old_value) = 0; + virtual glm::vec2 input_float2(const std::string &label, + glm::vec2 old_value) = 0; + virtual glm::vec3 input_float3(const std::string &label, + glm::vec3 old_value) = 0; + virtual glm::vec4 input_float4(const std::string &label, + glm::vec4 old_value) = 0; + virtual int drag_int(const std::string &label, + int old_value, + float speed, + int minimum, + int maximum) = 0; + virtual glm::ivec2 drag_int2(const std::string &label, + glm::ivec2 old_value, + float speed, + int minimum, + int maximum) = 0; + virtual glm::ivec3 drag_int3(const std::string &label, + glm::ivec3 old_value, + float speed, + int minimum, + int maximum) = 0; + virtual glm::ivec4 drag_int4(const std::string &label, + glm::ivec4 old_value, + float speed, + int minimum, + int maximum) = 0; + virtual float drag_float(const std::string &label, + float old_value, + float speed, + float minimum, + float maximum) = 0; + virtual glm::vec2 drag_float2(const std::string &label, + glm::vec2 old_value, + float speed, + float minimum, + float maximum) = 0; + virtual glm::vec3 drag_float3(const std::string &label, + glm::vec3 old_value, + float speed, + float minimum, + float maximum) = 0; + virtual glm::vec4 drag_float4(const std::string &label, + glm::vec4 old_value, + float speed, + float minimum, + float maximum) = 0; + virtual bool tree_node_push(const std::string &label) = 0; + virtual void tree_node_pop() = 0; + virtual void separator() = 0; + virtual void same_line() = 0; + virtual void indent() = 0; + virtual void unindent() = 0; + virtual void progress_bar(float fraction) = 0; + virtual bool collapsing_header(const std::string &label) = 0; + virtual bool selectable(const std::string &label, bool selected) = 0; + virtual bool radio_button(const std::string &label, bool active) = 0; + virtual int listbox(const std::string &label, + int current_item, + const std::vector &items, + int height_in_items) = 0; + virtual bool begin_tab_bar(const std::string &id) = 0; + virtual void end_tab_bar() = 0; + virtual bool begin_tab_item(const std::string &label) = 0; + virtual void end_tab_item() = 0; + virtual bool begin_table(const std::string &id, int columns) = 0; + virtual void end_table() = 0; + virtual void table_setup_column(const std::string &label) = 0; + virtual void table_headers_row() = 0; + virtual void table_next_row() = 0; + virtual bool table_next_column() = 0; virtual void prepare_for_next_frame() = 0; virtual ~GuiBase() = default; }; diff --git a/taichi/ui/ggui/gui.cpp b/taichi/ui/ggui/gui.cpp index f5bc10833880b..25f41b9c0026d 100644 --- a/taichi/ui/ggui/gui.cpp +++ b/taichi/ui/ggui/gui.cpp @@ -190,6 +190,36 @@ int Gui::slider_int(const std::string &name, ImGui::SliderInt(name.c_str(), &old_value, minimum, maximum); return old_value; } +glm::ivec2 Gui::slider_int2(const std::string &name, + glm::ivec2 old_value, + int minimum, + int maximum) { + if (!initialized()) { + return old_value; + } + ImGui::SliderInt2(name.c_str(), (int *)&old_value, minimum, maximum); + return old_value; +} +glm::ivec3 Gui::slider_int3(const std::string &name, + glm::ivec3 old_value, + int minimum, + int maximum) { + if (!initialized()) { + return old_value; + } + ImGui::SliderInt3(name.c_str(), (int *)&old_value, minimum, maximum); + return old_value; +} +glm::ivec4 Gui::slider_int4(const std::string &name, + glm::ivec4 old_value, + int minimum, + int maximum) { + if (!initialized()) { + return old_value; + } + ImGui::SliderInt4(name.c_str(), (int *)&old_value, minimum, maximum); + return old_value; +} float Gui::slider_float(const std::string &name, float old_value, float minimum, @@ -200,6 +230,36 @@ float Gui::slider_float(const std::string &name, ImGui::SliderFloat(name.c_str(), &old_value, minimum, maximum); return old_value; } +glm::vec2 Gui::slider_float2(const std::string &name, + glm::vec2 old_value, + float minimum, + float maximum) { + if (!initialized()) { + return old_value; + } + ImGui::SliderFloat2(name.c_str(), (float *)&old_value, minimum, maximum); + return old_value; +} +glm::vec3 Gui::slider_float3(const std::string &name, + glm::vec3 old_value, + float minimum, + float maximum) { + if (!initialized()) { + return old_value; + } + ImGui::SliderFloat3(name.c_str(), (float *)&old_value, minimum, maximum); + return old_value; +} +glm::vec4 Gui::slider_float4(const std::string &name, + glm::vec4 old_value, + float minimum, + float maximum) { + if (!initialized()) { + return old_value; + } + ImGui::SliderFloat4(name.c_str(), (float *)&old_value, minimum, maximum); + return old_value; +} glm::vec3 Gui::color_edit_3(const std::string &name, glm::vec3 old_value) { if (!initialized()) { return old_value; @@ -207,6 +267,27 @@ glm::vec3 Gui::color_edit_3(const std::string &name, glm::vec3 old_value) { ImGui::ColorEdit3(name.c_str(), (float *)&old_value); return old_value; } +glm::vec4 Gui::color_edit_4(const std::string &name, glm::vec4 old_value) { + if (!initialized()) { + return old_value; + } + ImGui::ColorEdit4(name.c_str(), (float *)&old_value); + return old_value; +} +glm::vec3 Gui::color_picker_3(const std::string &name, glm::vec3 old_value) { + if (!initialized()) { + return old_value; + } + ImGui::ColorPicker3(name.c_str(), (float *)&old_value); + return old_value; +} +glm::vec4 Gui::color_picker_4(const std::string &name, glm::vec4 old_value) { + if (!initialized()) { + return old_value; + } + ImGui::ColorPicker4(name.c_str(), (float *)&old_value); + return old_value; +} bool Gui::button(const std::string &text) { if (!initialized()) { return false; @@ -214,6 +295,333 @@ bool Gui::button(const std::string &text) { return ImGui::Button(text.c_str()); } +int Gui::combo(const std::string &label, + int current_item, + const std::vector &items) { + if (!initialized()) { + return current_item; + } + ImGui::Combo(label.c_str(), ¤t_item, items.data(), + static_cast(items.size())); + return current_item; +} + +int Gui::input_int(const std::string &label, int old_value) { + if (!initialized()) { + return old_value; + } + ImGui::InputInt(label.c_str(), &old_value); + return old_value; +} + +glm::ivec2 Gui::input_int2(const std::string &label, glm::ivec2 old_value) { + if (!initialized()) { + return old_value; + } + ImGui::InputInt2(label.c_str(), (int *)&old_value); + return old_value; +} + +glm::ivec3 Gui::input_int3(const std::string &label, glm::ivec3 old_value) { + if (!initialized()) { + return old_value; + } + ImGui::InputInt3(label.c_str(), (int *)&old_value); + return old_value; +} + +glm::ivec4 Gui::input_int4(const std::string &label, glm::ivec4 old_value) { + if (!initialized()) { + return old_value; + } + ImGui::InputInt4(label.c_str(), (int *)&old_value); + return old_value; +} + +float Gui::input_float(const std::string &label, float old_value) { + if (!initialized()) { + return old_value; + } + ImGui::InputFloat(label.c_str(), &old_value); + return old_value; +} + +glm::vec2 Gui::input_float2(const std::string &label, glm::vec2 old_value) { + if (!initialized()) { + return old_value; + } + ImGui::InputFloat2(label.c_str(), (float *)&old_value); + return old_value; +} + +glm::vec3 Gui::input_float3(const std::string &label, glm::vec3 old_value) { + if (!initialized()) { + return old_value; + } + ImGui::InputFloat3(label.c_str(), (float *)&old_value); + return old_value; +} + +glm::vec4 Gui::input_float4(const std::string &label, glm::vec4 old_value) { + if (!initialized()) { + return old_value; + } + ImGui::InputFloat4(label.c_str(), (float *)&old_value); + return old_value; +} + +int Gui::drag_int(const std::string &label, + int old_value, + float speed, + int minimum, + int maximum) { + if (!initialized()) { + return old_value; + } + ImGui::DragInt(label.c_str(), &old_value, speed, minimum, maximum); + return old_value; +} + +glm::ivec2 Gui::drag_int2(const std::string &label, + glm::ivec2 old_value, + float speed, + int minimum, + int maximum) { + if (!initialized()) { + return old_value; + } + ImGui::DragInt2(label.c_str(), (int *)&old_value, speed, minimum, maximum); + return old_value; +} + +glm::ivec3 Gui::drag_int3(const std::string &label, + glm::ivec3 old_value, + float speed, + int minimum, + int maximum) { + if (!initialized()) { + return old_value; + } + ImGui::DragInt3(label.c_str(), (int *)&old_value, speed, minimum, maximum); + return old_value; +} + +glm::ivec4 Gui::drag_int4(const std::string &label, + glm::ivec4 old_value, + float speed, + int minimum, + int maximum) { + if (!initialized()) { + return old_value; + } + ImGui::DragInt4(label.c_str(), (int *)&old_value, speed, minimum, maximum); + return old_value; +} + +float Gui::drag_float(const std::string &label, + float old_value, + float speed, + float minimum, + float maximum) { + if (!initialized()) { + return old_value; + } + ImGui::DragFloat(label.c_str(), &old_value, speed, minimum, maximum); + return old_value; +} + +glm::vec2 Gui::drag_float2(const std::string &label, + glm::vec2 old_value, + float speed, + float minimum, + float maximum) { + if (!initialized()) { + return old_value; + } + ImGui::DragFloat2(label.c_str(), (float *)&old_value, speed, minimum, + maximum); + return old_value; +} + +glm::vec3 Gui::drag_float3(const std::string &label, + glm::vec3 old_value, + float speed, + float minimum, + float maximum) { + if (!initialized()) { + return old_value; + } + ImGui::DragFloat3(label.c_str(), (float *)&old_value, speed, minimum, + maximum); + return old_value; +} + +glm::vec4 Gui::drag_float4(const std::string &label, + glm::vec4 old_value, + float speed, + float minimum, + float maximum) { + if (!initialized()) { + return old_value; + } + ImGui::DragFloat4(label.c_str(), (float *)&old_value, speed, minimum, + maximum); + return old_value; +} + +bool Gui::tree_node_push(const std::string &label) { + if (!initialized()) { + return false; + } + return ImGui::TreeNode(label.c_str()); +} + +void Gui::tree_node_pop() { + if (!initialized()) { + return; + } + ImGui::TreePop(); +} + +void Gui::separator() { + if (!initialized()) { + return; + } + ImGui::Separator(); +} + +void Gui::same_line() { + if (!initialized()) { + return; + } + ImGui::SameLine(); +} + +void Gui::indent() { + if (!initialized()) { + return; + } + ImGui::Indent(); +} + +void Gui::unindent() { + if (!initialized()) { + return; + } + ImGui::Unindent(); +} + +void Gui::progress_bar(float fraction) { + if (!initialized()) { + return; + } + ImGui::ProgressBar(fraction); +} + +bool Gui::collapsing_header(const std::string &label) { + if (!initialized()) { + return false; + } + return ImGui::CollapsingHeader(label.c_str()); +} + +bool Gui::selectable(const std::string &label, bool selected) { + if (!initialized()) { + return selected; + } + ImGui::Selectable(label.c_str(), &selected); + return selected; +} + +bool Gui::radio_button(const std::string &label, bool active) { + if (!initialized()) { + return false; + } + return ImGui::RadioButton(label.c_str(), active); +} + +int Gui::listbox(const std::string &label, + int current_item, + const std::vector &items, + int height_in_items) { + if (!initialized()) { + return current_item; + } + ImGui::ListBox(label.c_str(), ¤t_item, items.data(), + static_cast(items.size()), height_in_items); + return current_item; +} + +bool Gui::begin_tab_bar(const std::string &id) { + if (!initialized()) { + return false; + } + return ImGui::BeginTabBar(id.c_str()); +} + +void Gui::end_tab_bar() { + if (!initialized()) { + return; + } + ImGui::EndTabBar(); +} + +bool Gui::begin_tab_item(const std::string &label) { + if (!initialized()) { + return false; + } + return ImGui::BeginTabItem(label.c_str()); +} + +void Gui::end_tab_item() { + if (!initialized()) { + return; + } + ImGui::EndTabItem(); +} + +bool Gui::begin_table(const std::string &id, int columns) { + if (!initialized()) { + return false; + } + return ImGui::BeginTable(id.c_str(), columns); +} + +void Gui::end_table() { + if (!initialized()) { + return; + } + ImGui::EndTable(); +} + +void Gui::table_setup_column(const std::string &label) { + if (!initialized()) { + return; + } + ImGui::TableSetupColumn(label.c_str()); +} + +void Gui::table_headers_row() { + if (!initialized()) { + return; + } + ImGui::TableHeadersRow(); +} + +void Gui::table_next_row() { + if (!initialized()) { + return; + } + ImGui::TableNextRow(); +} + +bool Gui::table_next_column() { + if (!initialized()) { + return false; + } + return ImGui::TableNextColumn(); +} + void Gui::draw(taichi::lang::CommandList *cmd_list) { // Rendering ImGui::Render(); diff --git a/taichi/ui/ggui/gui.h b/taichi/ui/ggui/gui.h index e54314e570f7a..7b588f8882080 100644 --- a/taichi/ui/ggui/gui.h +++ b/taichi/ui/ggui/gui.h @@ -42,13 +42,122 @@ class TI_DLL_EXPORT Gui final : public GuiBase { int old_value, int minimum, int maximum) override; + glm::ivec2 slider_int2(const std::string &name, + glm::ivec2 old_value, + int minimum, + int maximum) override; + glm::ivec3 slider_int3(const std::string &name, + glm::ivec3 old_value, + int minimum, + int maximum) override; + glm::ivec4 slider_int4(const std::string &name, + glm::ivec4 old_value, + int minimum, + int maximum) override; float slider_float(const std::string &name, float old_value, float minimum, float maximum) override; - // TODO: consider renaming this? + glm::vec2 slider_float2(const std::string &name, + glm::vec2 old_value, + float minimum, + float maximum) override; + glm::vec3 slider_float3(const std::string &name, + glm::vec3 old_value, + float minimum, + float maximum) override; + glm::vec4 slider_float4(const std::string &name, + glm::vec4 old_value, + float minimum, + float maximum) override; glm::vec3 color_edit_3(const std::string &name, glm::vec3 old_value) override; + glm::vec4 color_edit_4(const std::string &name, glm::vec4 old_value) override; + glm::vec3 color_picker_3(const std::string &name, + glm::vec3 old_value) override; + glm::vec4 color_picker_4(const std::string &name, + glm::vec4 old_value) override; bool button(const std::string &text) override; + int combo(const std::string &label, + int current_item, + const std::vector &items) override; + int input_int(const std::string &label, int old_value) override; + glm::ivec2 input_int2(const std::string &label, + glm::ivec2 old_value) override; + glm::ivec3 input_int3(const std::string &label, + glm::ivec3 old_value) override; + glm::ivec4 input_int4(const std::string &label, + glm::ivec4 old_value) override; + float input_float(const std::string &label, float old_value) override; + glm::vec2 input_float2(const std::string &label, + glm::vec2 old_value) override; + glm::vec3 input_float3(const std::string &label, + glm::vec3 old_value) override; + glm::vec4 input_float4(const std::string &label, + glm::vec4 old_value) override; + int drag_int(const std::string &label, + int old_value, + float speed, + int minimum, + int maximum) override; + glm::ivec2 drag_int2(const std::string &label, + glm::ivec2 old_value, + float speed, + int minimum, + int maximum) override; + glm::ivec3 drag_int3(const std::string &label, + glm::ivec3 old_value, + float speed, + int minimum, + int maximum) override; + glm::ivec4 drag_int4(const std::string &label, + glm::ivec4 old_value, + float speed, + int minimum, + int maximum) override; + float drag_float(const std::string &label, + float old_value, + float speed, + float minimum, + float maximum) override; + glm::vec2 drag_float2(const std::string &label, + glm::vec2 old_value, + float speed, + float minimum, + float maximum) override; + glm::vec3 drag_float3(const std::string &label, + glm::vec3 old_value, + float speed, + float minimum, + float maximum) override; + glm::vec4 drag_float4(const std::string &label, + glm::vec4 old_value, + float speed, + float minimum, + float maximum) override; + bool tree_node_push(const std::string &label) override; + void tree_node_pop() override; + void separator() override; + void same_line() override; + void indent() override; + void unindent() override; + void progress_bar(float fraction) override; + bool collapsing_header(const std::string &label) override; + bool selectable(const std::string &label, bool selected) override; + bool radio_button(const std::string &label, bool active) override; + int listbox(const std::string &label, + int current_item, + const std::vector &items, + int height_in_items) override; + bool begin_tab_bar(const std::string &id) override; + void end_tab_bar() override; + bool begin_tab_item(const std::string &label) override; + void end_tab_item() override; + bool begin_table(const std::string &id, int columns) override; + void end_table() override; + void table_setup_column(const std::string &label) override; + void table_headers_row() override; + void table_next_row() override; + bool table_next_column() override; void draw(taichi::lang::CommandList *cmd_list); diff --git a/taichi/ui/ggui/gui_metal.h b/taichi/ui/ggui/gui_metal.h index 039c87fe9bc3a..35b5ba4f637eb 100644 --- a/taichi/ui/ggui/gui_metal.h +++ b/taichi/ui/ggui/gui_metal.h @@ -36,13 +36,122 @@ class TI_DLL_EXPORT GuiMetal final : public GuiBase { int old_value, int minimum, int maximum) override; + glm::ivec2 slider_int2(const std::string &name, + glm::ivec2 old_value, + int minimum, + int maximum) override; + glm::ivec3 slider_int3(const std::string &name, + glm::ivec3 old_value, + int minimum, + int maximum) override; + glm::ivec4 slider_int4(const std::string &name, + glm::ivec4 old_value, + int minimum, + int maximum) override; float slider_float(const std::string &name, float old_value, float minimum, float maximum) override; - // TODO: consider renaming this? + glm::vec2 slider_float2(const std::string &name, + glm::vec2 old_value, + float minimum, + float maximum) override; + glm::vec3 slider_float3(const std::string &name, + glm::vec3 old_value, + float minimum, + float maximum) override; + glm::vec4 slider_float4(const std::string &name, + glm::vec4 old_value, + float minimum, + float maximum) override; glm::vec3 color_edit_3(const std::string &name, glm::vec3 old_value) override; + glm::vec4 color_edit_4(const std::string &name, glm::vec4 old_value) override; + glm::vec3 color_picker_3(const std::string &name, + glm::vec3 old_value) override; + glm::vec4 color_picker_4(const std::string &name, + glm::vec4 old_value) override; bool button(const std::string &text) override; + int combo(const std::string &label, + int current_item, + const std::vector &items) override; + int input_int(const std::string &label, int old_value) override; + glm::ivec2 input_int2(const std::string &label, + glm::ivec2 old_value) override; + glm::ivec3 input_int3(const std::string &label, + glm::ivec3 old_value) override; + glm::ivec4 input_int4(const std::string &label, + glm::ivec4 old_value) override; + float input_float(const std::string &label, float old_value) override; + glm::vec2 input_float2(const std::string &label, + glm::vec2 old_value) override; + glm::vec3 input_float3(const std::string &label, + glm::vec3 old_value) override; + glm::vec4 input_float4(const std::string &label, + glm::vec4 old_value) override; + int drag_int(const std::string &label, + int old_value, + float speed, + int minimum, + int maximum) override; + glm::ivec2 drag_int2(const std::string &label, + glm::ivec2 old_value, + float speed, + int minimum, + int maximum) override; + glm::ivec3 drag_int3(const std::string &label, + glm::ivec3 old_value, + float speed, + int minimum, + int maximum) override; + glm::ivec4 drag_int4(const std::string &label, + glm::ivec4 old_value, + float speed, + int minimum, + int maximum) override; + float drag_float(const std::string &label, + float old_value, + float speed, + float minimum, + float maximum) override; + glm::vec2 drag_float2(const std::string &label, + glm::vec2 old_value, + float speed, + float minimum, + float maximum) override; + glm::vec3 drag_float3(const std::string &label, + glm::vec3 old_value, + float speed, + float minimum, + float maximum) override; + glm::vec4 drag_float4(const std::string &label, + glm::vec4 old_value, + float speed, + float minimum, + float maximum) override; + bool tree_node_push(const std::string &label) override; + void tree_node_pop() override; + void separator() override; + void same_line() override; + void indent() override; + void unindent() override; + void progress_bar(float fraction) override; + bool collapsing_header(const std::string &label) override; + bool selectable(const std::string &label, bool selected) override; + bool radio_button(const std::string &label, bool active) override; + int listbox(const std::string &label, + int current_item, + const std::vector &items, + int height_in_items) override; + bool begin_tab_bar(const std::string &id) override; + void end_tab_bar() override; + bool begin_tab_item(const std::string &label) override; + void end_tab_item() override; + bool begin_table(const std::string &id, int columns) override; + void end_table() override; + void table_setup_column(const std::string &label) override; + void table_headers_row() override; + void table_next_row() override; + bool table_next_column() override; void prepare_for_next_frame() override; diff --git a/taichi/ui/ggui/gui_metal.mm b/taichi/ui/ggui/gui_metal.mm index 61cbfb1b7c5b5..d39d95bc71755 100644 --- a/taichi/ui/ggui/gui_metal.mm +++ b/taichi/ui/ggui/gui_metal.mm @@ -78,19 +78,232 @@ ImGui::SliderInt(name.c_str(), &old_value, minimum, maximum); return old_value; } +glm::ivec2 GuiMetal::slider_int2(const std::string &name, glm::ivec2 old_value, + int minimum, int maximum) { + ImGui::SliderInt2(name.c_str(), (int *)&old_value, minimum, maximum); + return old_value; +} +glm::ivec3 GuiMetal::slider_int3(const std::string &name, glm::ivec3 old_value, + int minimum, int maximum) { + ImGui::SliderInt3(name.c_str(), (int *)&old_value, minimum, maximum); + return old_value; +} +glm::ivec4 GuiMetal::slider_int4(const std::string &name, glm::ivec4 old_value, + int minimum, int maximum) { + ImGui::SliderInt4(name.c_str(), (int *)&old_value, minimum, maximum); + return old_value; +} float GuiMetal::slider_float(const std::string &name, float old_value, float minimum, float maximum) { ImGui::SliderFloat(name.c_str(), &old_value, minimum, maximum); return old_value; } +glm::vec2 GuiMetal::slider_float2(const std::string &name, glm::vec2 old_value, + float minimum, float maximum) { + ImGui::SliderFloat2(name.c_str(), (float *)&old_value, minimum, maximum); + return old_value; +} +glm::vec3 GuiMetal::slider_float3(const std::string &name, glm::vec3 old_value, + float minimum, float maximum) { + ImGui::SliderFloat3(name.c_str(), (float *)&old_value, minimum, maximum); + return old_value; +} +glm::vec4 GuiMetal::slider_float4(const std::string &name, glm::vec4 old_value, + float minimum, float maximum) { + ImGui::SliderFloat4(name.c_str(), (float *)&old_value, minimum, maximum); + return old_value; +} glm::vec3 GuiMetal::color_edit_3(const std::string &name, glm::vec3 old_value) { ImGui::ColorEdit3(name.c_str(), (float *)&old_value); return old_value; } +glm::vec4 GuiMetal::color_edit_4(const std::string &name, glm::vec4 old_value) { + ImGui::ColorEdit4(name.c_str(), (float *)&old_value); + return old_value; +} +glm::vec3 GuiMetal::color_picker_3(const std::string &name, + glm::vec3 old_value) { + ImGui::ColorPicker3(name.c_str(), (float *)&old_value); + return old_value; +} +glm::vec4 GuiMetal::color_picker_4(const std::string &name, + glm::vec4 old_value) { + ImGui::ColorPicker4(name.c_str(), (float *)&old_value); + return old_value; +} bool GuiMetal::button(const std::string &text) { return ImGui::Button(text.c_str()); } +int GuiMetal::combo(const std::string &label, int current_item, + const std::vector &items) { + ImGui::Combo(label.c_str(), ¤t_item, items.data(), + static_cast(items.size())); + return current_item; +} + +int GuiMetal::input_int(const std::string &label, int old_value) { + ImGui::InputInt(label.c_str(), &old_value); + return old_value; +} + +glm::ivec2 GuiMetal::input_int2(const std::string &label, + glm::ivec2 old_value) { + ImGui::InputInt2(label.c_str(), (int *)&old_value); + return old_value; +} + +glm::ivec3 GuiMetal::input_int3(const std::string &label, + glm::ivec3 old_value) { + ImGui::InputInt3(label.c_str(), (int *)&old_value); + return old_value; +} + +glm::ivec4 GuiMetal::input_int4(const std::string &label, + glm::ivec4 old_value) { + ImGui::InputInt4(label.c_str(), (int *)&old_value); + return old_value; +} + +float GuiMetal::input_float(const std::string &label, float old_value) { + ImGui::InputFloat(label.c_str(), &old_value); + return old_value; +} + +glm::vec2 GuiMetal::input_float2(const std::string &label, + glm::vec2 old_value) { + ImGui::InputFloat2(label.c_str(), (float *)&old_value); + return old_value; +} + +glm::vec3 GuiMetal::input_float3(const std::string &label, + glm::vec3 old_value) { + ImGui::InputFloat3(label.c_str(), (float *)&old_value); + return old_value; +} + +glm::vec4 GuiMetal::input_float4(const std::string &label, + glm::vec4 old_value) { + ImGui::InputFloat4(label.c_str(), (float *)&old_value); + return old_value; +} + +int GuiMetal::drag_int(const std::string &label, int old_value, float speed, + int minimum, int maximum) { + ImGui::DragInt(label.c_str(), &old_value, speed, minimum, maximum); + return old_value; +} + +glm::ivec2 GuiMetal::drag_int2(const std::string &label, glm::ivec2 old_value, + float speed, int minimum, int maximum) { + ImGui::DragInt2(label.c_str(), (int *)&old_value, speed, minimum, maximum); + return old_value; +} + +glm::ivec3 GuiMetal::drag_int3(const std::string &label, glm::ivec3 old_value, + float speed, int minimum, int maximum) { + ImGui::DragInt3(label.c_str(), (int *)&old_value, speed, minimum, maximum); + return old_value; +} + +glm::ivec4 GuiMetal::drag_int4(const std::string &label, glm::ivec4 old_value, + float speed, int minimum, int maximum) { + ImGui::DragInt4(label.c_str(), (int *)&old_value, speed, minimum, maximum); + return old_value; +} + +float GuiMetal::drag_float(const std::string &label, float old_value, + float speed, float minimum, float maximum) { + ImGui::DragFloat(label.c_str(), &old_value, speed, minimum, maximum); + return old_value; +} + +glm::vec2 GuiMetal::drag_float2(const std::string &label, glm::vec2 old_value, + float speed, float minimum, float maximum) { + ImGui::DragFloat2(label.c_str(), (float *)&old_value, speed, minimum, + maximum); + return old_value; +} + +glm::vec3 GuiMetal::drag_float3(const std::string &label, glm::vec3 old_value, + float speed, float minimum, float maximum) { + ImGui::DragFloat3(label.c_str(), (float *)&old_value, speed, minimum, + maximum); + return old_value; +} + +glm::vec4 GuiMetal::drag_float4(const std::string &label, glm::vec4 old_value, + float speed, float minimum, float maximum) { + ImGui::DragFloat4(label.c_str(), (float *)&old_value, speed, minimum, + maximum); + return old_value; +} + +bool GuiMetal::tree_node_push(const std::string &label) { + return ImGui::TreeNode(label.c_str()); +} + +void GuiMetal::tree_node_pop() { ImGui::TreePop(); } + +void GuiMetal::separator() { ImGui::Separator(); } + +void GuiMetal::same_line() { ImGui::SameLine(); } + +void GuiMetal::indent() { ImGui::Indent(); } + +void GuiMetal::unindent() { ImGui::Unindent(); } + +void GuiMetal::progress_bar(float fraction) { ImGui::ProgressBar(fraction); } + +bool GuiMetal::collapsing_header(const std::string &label) { + return ImGui::CollapsingHeader(label.c_str()); +} + +bool GuiMetal::selectable(const std::string &label, bool selected) { + ImGui::Selectable(label.c_str(), &selected); + return selected; +} + +bool GuiMetal::radio_button(const std::string &label, bool active) { + return ImGui::RadioButton(label.c_str(), active); +} + +int GuiMetal::listbox(const std::string &label, int current_item, + const std::vector &items, + int height_in_items) { + ImGui::ListBox(label.c_str(), ¤t_item, items.data(), + static_cast(items.size()), height_in_items); + return current_item; +} + +bool GuiMetal::begin_tab_bar(const std::string &id) { + return ImGui::BeginTabBar(id.c_str()); +} + +void GuiMetal::end_tab_bar() { ImGui::EndTabBar(); } + +bool GuiMetal::begin_tab_item(const std::string &label) { + return ImGui::BeginTabItem(label.c_str()); +} + +void GuiMetal::end_tab_item() { ImGui::EndTabItem(); } + +bool GuiMetal::begin_table(const std::string &id, int columns) { + return ImGui::BeginTable(id.c_str(), columns); +} + +void GuiMetal::end_table() { ImGui::EndTable(); } + +void GuiMetal::table_setup_column(const std::string &label) { + ImGui::TableSetupColumn(label.c_str()); +} + +void GuiMetal::table_headers_row() { ImGui::TableHeadersRow(); } + +void GuiMetal::table_next_row() { ImGui::TableNextRow(); } + +bool GuiMetal::table_next_column() { return ImGui::TableNextColumn(); } + void GuiMetal::draw(taichi::lang::CommandList *cmd_list) { ImGui_ImplMetal_NewFrame(current_rpd_);