Skip to content

Commit

Permalink
Expose GUI ordering API (#91)
Browse files Browse the repository at this point in the history
* Expose GUI order API

* Revert accident

* Fix example
  • Loading branch information
brentyi authored Aug 28, 2023
1 parent 30aa5a9 commit 5bd1d2e
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 22 deletions.
1 change: 0 additions & 1 deletion examples/02_gui.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ def main():
"Size",
initial_value=(1.0, 1.0, 1.0),
step=0.25,
lock=True,
)
with server.add_gui_folder("Text toggle"):
gui_checkbox_hide = server.add_gui_checkbox(
Expand Down
97 changes: 77 additions & 20 deletions viser/_gui_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,23 @@ class _RootGuiContainer:
_children: Dict[str, SupportsRemoveProtocol]


_global_order_counter = 0


def _apply_default_order(order: Optional[float]) -> float:
"""Apply default ordering logic for GUI elements.
If `order` is set to a float, this function is a no-op and returns it back.
Otherwise, we increment and return the value of a global counter.
"""
if order is not None:
return order

global _global_order_counter
_global_order_counter += 1
return _global_order_counter


class GuiApi(abc.ABC):
_target_container_from_thread_id: Dict[int, str] = {}
"""ID of container to put GUI elements into."""
Expand Down Expand Up @@ -199,12 +216,15 @@ def gui_folder(self, label: str) -> GuiFolderHandle:
)
return self.add_gui_folder(label)

def add_gui_folder(self, label: str) -> GuiFolderHandle:
def add_gui_folder(
self, label: str, order: Optional[float] = None
) -> GuiFolderHandle:
"""Add a folder, and return a handle that can be used to populate it."""
folder_container_id = _make_unique_id()
order = _apply_default_order(order)
self._get_api()._queue(
_messages.GuiAddFolderMessage(
order=time.time(),
order=order,
id=folder_container_id,
label=label,
container_id=self._get_container_id(),
Expand All @@ -214,18 +234,21 @@ def add_gui_folder(self, label: str) -> GuiFolderHandle:
_gui_api=self,
_id=folder_container_id,
_parent_container_id=self._get_container_id(),
_order=order,
)

def add_gui_modal(
self,
title: str,
order: Optional[float] = None,
) -> GuiModalHandle:
"""Show a modal window, which can be useful for popups and messages, then return
a handle that can be used to populate it."""
modal_container_id = _make_unique_id()
order = _apply_default_order(order)
self._get_api()._queue(
_messages.GuiModalMessage(
order=time.time(),
order=order,
id=modal_container_id,
title=title,
)
Expand All @@ -235,28 +258,36 @@ def add_gui_modal(
_id=modal_container_id,
)

def add_gui_tab_group(self) -> GuiTabGroupHandle:
def add_gui_tab_group(
self,
order: Optional[float] = None,
) -> GuiTabGroupHandle:
"""Add a tab group."""
tab_group_id = _make_unique_id()
order = _apply_default_order(order)
return GuiTabGroupHandle(
_tab_group_id=tab_group_id,
_labels=[],
_icons_base64=[],
_tabs=[],
_gui_api=self,
_container_id=self._get_container_id(),
_order=order,
)

def add_gui_markdown(
self, markdown: str, image_root: Optional[Path] = None
self,
markdown: str,
image_root: Optional[Path] = None,
order: Optional[float] = None,
) -> GuiMarkdownHandle:
"""Add markdown to the GUI."""
markdown = _parse_markdown(markdown, image_root)

markdown_id = _make_unique_id()
order = _apply_default_order(order)
self._get_api()._queue(
_messages.GuiAddMarkdownMessage(
order=time.time(),
order=order,
id=markdown_id,
markdown=markdown,
container_id=self._get_container_id(),
Expand All @@ -267,6 +298,7 @@ def add_gui_markdown(
_id=markdown_id,
_visible=True,
_container_id=self._get_container_id(),
_order=order,
)

def add_gui_button(
Expand Down Expand Up @@ -294,17 +326,19 @@ def add_gui_button(
]
] = None,
icon: Optional[Icon] = None,
order: Optional[float] = None,
) -> GuiButtonHandle:
"""Add a button to the GUI. The value of this input is set to `True` every time
it is clicked; to detect clicks, we can manually set it back to `False`."""

# Re-wrap the GUI handle with a button interface.
id = _make_unique_id()
order = _apply_default_order(order)
return GuiButtonHandle(
self._create_gui_input(
initial_value=False,
message=_messages.GuiAddButtonMessage(
order=time.time(),
order=order,
id=id,
label=label,
container_id=self._get_container_id(),
Expand Down Expand Up @@ -332,6 +366,7 @@ def add_gui_button_group(
visible: bool = True,
disabled: bool = False,
hint: Optional[str] = None,
order: Optional[float] = None,
) -> GuiButtonGroupHandle[TLiteralString]:
...

Expand All @@ -343,6 +378,7 @@ def add_gui_button_group(
visible: bool = True,
disabled: bool = False,
hint: Optional[str] = None,
order: Optional[float] = None,
) -> GuiButtonGroupHandle[TString]:
...

Expand All @@ -353,15 +389,17 @@ def add_gui_button_group(
visible: bool = True,
disabled: bool = False,
hint: Optional[str] = None,
order: Optional[float] = None,
) -> GuiButtonGroupHandle[Any]: # Return types are specified in overloads.
"""Add a button group to the GUI."""
initial_value = options[0]
id = _make_unique_id()
order = _apply_default_order(order)
return GuiButtonGroupHandle(
self._create_gui_input(
initial_value,
message=_messages.GuiAddButtonGroupMessage(
order=time.time(),
order=order,
id=id,
label=label,
container_id=self._get_container_id(),
Expand All @@ -381,14 +419,16 @@ def add_gui_checkbox(
disabled: bool = False,
visible: bool = True,
hint: Optional[str] = None,
order: Optional[float] = None,
) -> GuiInputHandle[bool]:
"""Add a checkbox to the GUI."""
assert isinstance(initial_value, bool)
id = _make_unique_id()
order = _apply_default_order(order)
return self._create_gui_input(
initial_value,
message=_messages.GuiAddCheckboxMessage(
order=time.time(),
order=order,
id=id,
label=label,
container_id=self._get_container_id(),
Expand All @@ -406,14 +446,16 @@ def add_gui_text(
disabled: bool = False,
visible: bool = True,
hint: Optional[str] = None,
order: Optional[float] = None,
) -> GuiInputHandle[str]:
"""Add a text input to the GUI."""
assert isinstance(initial_value, str)
id = _make_unique_id()
order = _apply_default_order(order)
return self._create_gui_input(
initial_value,
message=_messages.GuiAddTextMessage(
order=time.time(),
order=order,
id=id,
label=label,
container_id=self._get_container_id(),
Expand All @@ -434,6 +476,7 @@ def add_gui_number(
disabled: bool = False,
visible: bool = True,
hint: Optional[str] = None,
order: Optional[float] = None,
) -> GuiInputHandle[IntOrFloat]:
"""Add a number input to the GUI, with user-specifiable bound and precision parameters."""
assert isinstance(initial_value, (int, float))
Expand All @@ -454,10 +497,11 @@ def add_gui_number(
assert step is not None

id = _make_unique_id()
order = _apply_default_order(order)
return self._create_gui_input(
initial_value=initial_value,
message=_messages.GuiAddNumberMessage(
order=time.time(),
order=order,
id=id,
label=label,
container_id=self._get_container_id(),
Expand All @@ -483,12 +527,14 @@ def add_gui_vector2(
disabled: bool = False,
visible: bool = True,
hint: Optional[str] = None,
order: Optional[float] = None,
) -> GuiInputHandle[Tuple[float, float]]:
"""Add a length-2 vector input to the GUI."""
initial_value = cast_vector(initial_value, 2)
min = cast_vector(min, 2) if min is not None else None
max = cast_vector(max, 2) if max is not None else None
id = _make_unique_id()
order = _apply_default_order(order)

if step is None:
possible_steps: List[float] = []
Expand All @@ -502,7 +548,7 @@ def add_gui_vector2(
return self._create_gui_input(
initial_value,
message=_messages.GuiAddVector2Message(
order=time.time(),
order=order,
id=id,
label=label,
container_id=self._get_container_id(),
Expand All @@ -524,16 +570,17 @@ def add_gui_vector3(
min: Tuple[float, float, float] | onp.ndarray | None = None,
max: Tuple[float, float, float] | onp.ndarray | None = None,
step: Optional[float] = None,
lock: bool = False,
disabled: bool = False,
visible: bool = True,
hint: Optional[str] = None,
order: Optional[float] = None,
) -> GuiInputHandle[Tuple[float, float, float]]:
"""Add a length-3 vector input to the GUI."""
initial_value = cast_vector(initial_value, 2)
min = cast_vector(min, 3) if min is not None else None
max = cast_vector(max, 3) if max is not None else None
id = _make_unique_id()
order = _apply_default_order(order)

if step is None:
possible_steps: List[float] = []
Expand All @@ -547,7 +594,7 @@ def add_gui_vector3(
return self._create_gui_input(
initial_value,
message=_messages.GuiAddVector3Message(
order=time.time(),
order=order,
id=id,
label=label,
container_id=self._get_container_id(),
Expand All @@ -572,6 +619,7 @@ def add_gui_dropdown(
disabled: bool = False,
visible: bool = True,
hint: Optional[str] = None,
order: Optional[float] = None,
) -> GuiDropdownHandle[TLiteralString]:
...

Expand All @@ -583,6 +631,8 @@ def add_gui_dropdown(
initial_value: Optional[TString] = None,
disabled: bool = False,
visible: bool = True,
hint: Optional[str] = None,
order: Optional[float] = None,
) -> GuiDropdownHandle[TString]:
...

Expand All @@ -594,16 +644,18 @@ def add_gui_dropdown(
disabled: bool = False,
visible: bool = True,
hint: Optional[str] = None,
order: Optional[float] = None,
) -> GuiDropdownHandle[Any]: # Output type is specified in overloads.
"""Add a dropdown to the GUI."""
if initial_value is None:
initial_value = options[0]
id = _make_unique_id()
order = _apply_default_order(order)
return GuiDropdownHandle(
self._create_gui_input(
initial_value,
message=_messages.GuiAddDropdownMessage(
order=time.time(),
order=order,
id=id,
label=label,
container_id=self._get_container_id(),
Expand All @@ -627,6 +679,7 @@ def add_gui_slider(
disabled: bool = False,
visible: bool = True,
hint: Optional[str] = None,
order: Optional[float] = None,
) -> GuiInputHandle[IntOrFloat]:
"""Add a slider to the GUI. Types of the min, max, step, and initial value should match."""
assert max >= min
Expand All @@ -646,12 +699,12 @@ def add_gui_slider(
#
# assert type(min) == type(max) == type(step) == type(initial_value)

# Re-wrap the GUI handle with a button interface.
id = _make_unique_id()
order = _apply_default_order(order)
return self._create_gui_input(
initial_value=initial_value,
message=_messages.GuiAddSliderMessage(
order=time.time(),
order=order,
id=id,
label=label,
container_id=self._get_container_id(),
Expand All @@ -674,13 +727,15 @@ def add_gui_rgb(
disabled: bool = False,
visible: bool = True,
hint: Optional[str] = None,
order: Optional[float] = None,
) -> GuiInputHandle[Tuple[int, int, int]]:
"""Add an RGB picker to the GUI."""
id = _make_unique_id()
order = _apply_default_order(order)
return self._create_gui_input(
initial_value,
message=_messages.GuiAddRgbMessage(
order=time.time(),
order=order,
id=id,
label=label,
container_id=self._get_container_id(),
Expand All @@ -698,13 +753,15 @@ def add_gui_rgba(
disabled: bool = False,
visible: bool = True,
hint: Optional[str] = None,
order: Optional[float] = None,
) -> GuiInputHandle[Tuple[int, int, int, int]]:
"""Add an RGBA picker to the GUI."""
id = _make_unique_id()
order = _apply_default_order(order)
return self._create_gui_input(
initial_value,
message=_messages.GuiAddRgbaMessage(
order=time.time(),
order=order,
id=id,
label=label,
container_id=self._get_container_id(),
Expand Down
Loading

0 comments on commit 5bd1d2e

Please sign in to comment.