Exemple #1
0
def render_labeled_variable(
    id: str,
    label: str,
    variable: Variable,
    value: T,
    formatter: VariableFormatter,
    is_edited: bool,
    label_width=80,
    value_width=80,
) -> Tuple[Maybe[T], bool]:
    ig.push_id(id)

    ig.selectable(label + '##label', width=label_width)

    if ig.begin_drag_drop_source():
        ig.text(label)
        ig.set_drag_drop_payload('ve-var', variable.to_bytes())
        ig.end_drag_drop_source()

    ig.same_line()

    cell_width = value_width
    cell_height = ig.get_text_line_height(
    ) + 2 * ig.get_style().frame_padding[1]

    cell_cursor_pos = ig.get_cursor_pos()
    cell_cursor_pos = (
        cell_cursor_pos[0] + ig.get_window_position()[0] - ig.get_scroll_x(),
        cell_cursor_pos[1] + ig.get_window_position()[1] - ig.get_scroll_y(),
    )

    changed_data, _, _ = render_variable_value(
        'value',
        value,
        formatter,
        (cell_width, cell_height),
    )

    clear_edit = is_edited and ig.is_item_hovered() and ig.is_mouse_down(2)

    if is_edited:
        dl = ig.get_window_draw_list()
        spacing = ig.get_style().item_spacing
        spacing = (spacing[0] / 2, spacing[1] / 2)
        dl.add_rect(
            cell_cursor_pos[0] - spacing[0],
            cell_cursor_pos[1] - spacing[1],
            cell_cursor_pos[0] + cell_width + spacing[0] - 1,
            cell_cursor_pos[1] + cell_height + spacing[1] - 1,
            ig.get_color_u32_rgba(0.8, 0.6, 0, 1),
        )

    ig.pop_id()
    return changed_data, clear_edit
Exemple #2
0
def render_object_slots(
  id: str,
  behaviors: List[Optional[ObjectBehavior]],
  behavior_name: Callable[[ObjectBehavior], str],
) -> Optional[int]:
  ig.push_id(id)

  button_size = 50
  window_left = ig.get_window_position()[0]
  window_right = window_left + ig.get_window_content_region_max()[0]
  prev_item_right = window_left
  style = ig.get_style()

  result = None

  for slot, behavior in enumerate(behaviors):
    item_right = prev_item_right + style.item_spacing[0] + button_size
    if item_right > window_right:
      prev_item_right = window_left
    elif slot != 0:
      ig.same_line()
    prev_item_right = prev_item_right + style.item_spacing[0] + button_size

    if behavior is None:
      label = str(slot)
    else:
      label = str(slot) + '\n' + behavior_name(behavior)

    if ig.button(label + '##slot-' + str(slot), button_size, button_size):
      result = slot

  ig.pop_id()
  return result
Exemple #3
0
 def is_mouse_in_window(self) -> bool:
     if not ig.global_mouse_capture():
         return False
     window_x, window_y = cast(Iterable[float], ig.get_window_position())
     window_w, window_h = cast(Iterable[float], ig.get_window_size())
     return self.mouse_pos[0] >= window_x and self.mouse_pos[0] < window_x + window_w and \
       self.mouse_pos[1] >= window_y and self.mouse_pos[1] < window_y + window_h
Exemple #4
0
def get_viewport(framebuffer_size: Tuple[int, int]) -> core.Viewport:
    window_pos = tuple(map(int, ig.get_window_position()))
    window_size = tuple(map(int, ig.get_window_size()))

    viewport = core.Viewport()
    viewport.x = window_pos[0]
    viewport.y = window_pos[1]
    viewport.width = window_size[0]
    viewport.height = window_size[1]

    return viewport
Exemple #5
0
def render_frame_slider(
    id: str,
    current_frame: int,
    num_frames: int,
    loaded_frames: List[int] = [],
) -> Maybe[int]:
    ig.push_id(id)

    pos = ig.get_cursor_pos()
    pos = (
        pos[0] + ig.get_window_position()[0],
        pos[1] + ig.get_window_position()[1] - ig.get_scroll_y(),
    )
    width = ig.get_content_region_available_width()

    ig.push_item_width(width)
    changed, new_frame = ig.slider_int(
        '##slider',
        current_frame,
        0,
        num_frames - 1,
    )
    ig.pop_item_width()

    dl = ig.get_window_draw_list()
    for frame in loaded_frames:
        line_pos = pos[0] + frame / num_frames * width
        dl.add_line(
            line_pos,
            pos[1] + 13,
            line_pos,
            pos[1] + 18,
            ig.get_color_u32_rgba(1, 0, 0, 1),
        )

    ig.pop_id()

    if changed:
        return Just(new_frame)
    else:
        return None
Exemple #6
0
def render_input_text_with_error(
    id: str,
    value: str,
    buffer_size: int,
    width: int,
    validate: Callable[[str], T],
) -> Maybe[T]:
    ig.push_id(id)

    cursor_pos = ig.get_cursor_pos()
    cursor_pos = (
        ig.get_window_position()[0] + cursor_pos[0],
        ig.get_window_position()[1] + cursor_pos[1] - ig.get_scroll_y(),
    )

    ig.push_item_width(width)
    changed, new_value = ig.input_text('##input', value, buffer_size)
    ig.pop_item_width()

    result_value = None
    if changed:
        try:
            result_value = Just(validate(new_value))
        except:
            # TODO: Show error message
            dl = ig.get_window_draw_list()
            dl.add_rect(
                cursor_pos[0],
                cursor_pos[1],
                cursor_pos[0] + width,
                cursor_pos[1] + ig.get_text_line_height() +
                2 * ig.get_style().frame_padding[1],
                ig.get_color_u32_rgba(1, 0, 0, 1),
            )
            new_value = value

    ig.pop_id()
    return result_value
Exemple #7
0
def render_variable_cell(
    id: str,
    value: T,
    formatter: VariableFormatter,
    cell_size: Tuple[int, int],
    is_selected: bool,
    frame: Optional[int] = None,
    highlight_range: Optional[Tuple[range, ig.Color4f]] = None,
) -> Tuple[Maybe[T], bool, bool, bool]:
    ig.push_id(id)

    window_pos = ig.get_window_position()
    item_spacing = ig.get_style().item_spacing

    cell_cursor_pos = ig.get_cursor_pos()
    cell_cursor_pos = (
        cell_cursor_pos.x + window_pos.x - item_spacing.x,
        cell_cursor_pos.y + window_pos.y - ig.get_scroll_y() - item_spacing.y,
    )

    if highlight_range is not None:
        assert frame is not None
        frames, color = highlight_range
        margin = 5
        offset_top = margin if frame == frames.start else 0
        offset_bottom = margin if frame == frames.stop - 1 else 0
        dl = ig.get_window_draw_list()
        dl.add_rect_filled(
            cell_cursor_pos[0] + margin,
            cell_cursor_pos[1] + offset_top,
            cell_cursor_pos[0] + cell_size[0] - margin,
            cell_cursor_pos[1] + cell_size[1] - offset_bottom,
            ig.get_color_u32_rgba(*color),
        )

    changed_data, selected, pressed = render_variable_value(
        'value',
        value,
        formatter,
        (
            cell_size[0] - 2 * item_spacing.x,
            cell_size[1] - 2 * item_spacing.y,
        ),
        highlight=is_selected,
    )

    clear_edit = ig.is_item_hovered() and ig.is_mouse_down(2)

    ig.pop_id()
    return changed_data, clear_edit, selected, pressed
Exemple #8
0
def get_normalized_mouse_pos() -> Optional[Tuple[float, float]]:
    if not ig.global_mouse_capture():
        return None
    window_pos = tuple(map(int, ig.get_window_position()))
    window_size = tuple(map(int, ig.get_window_size()))
    mouse_pos = tuple(map(float, ig.get_mouse_pos()))
    mouse_pos = (
        mouse_pos[0] - window_pos[0],
        window_size[1] - mouse_pos[1] + window_pos[1],
    )
    mouse_pos = (
        2 * mouse_pos[0] / window_size[0] - 1,
        2 * mouse_pos[1] / window_size[1] - 1,
    )
    if any(c < -1 or c > 1 for c in mouse_pos):
        return None
    return mouse_pos
Exemple #9
0
    def get_drag_amount(self) -> Tuple[float, float]:
        mouse_was_down = self.mouse_down
        last_mouse_pos = self.mouse_pos
        self.mouse_down = ig.is_mouse_down() and ig.global_mouse_capture()
        self.mouse_pos = ig.get_mouse_pos()

        if self.dragging:
            if not self.mouse_down:
                self.dragging = False
            return (
                self.mouse_pos[0] - last_mouse_pos[0],
                self.mouse_pos[1] - last_mouse_pos[1],
            )

        elif not mouse_was_down and self.mouse_down and not ig.is_any_item_hovered(
        ):
            window_x, window_y = ig.get_window_position()
            window_w, window_h = ig.get_window_size()
            if self.mouse_pos[0] >= window_x and self.mouse_pos[0] < window_x + window_w and \
                self.mouse_pos[1] >= window_y and self.mouse_pos[1] < window_y + window_h:
                self.dragging = True

        return (0, 0)
Exemple #10
0
def _render_text(
    value: T,
    formatter: VariableFormatter,
    size: Tuple[int, int],
    highlight: bool,
) -> Tuple[Maybe[T], bool, bool]:
    editing = use_state('editing', False)
    initial_focus = use_state('initial-focus', False)

    if not editing.value:
        clicked, _ = ig.selectable(
            dcast(str, formatter.output(value)) + '##text',
            highlight,
            width=size[0],
            height=size[1],
            flags=ig.SELECTABLE_ALLOW_DOUBLE_CLICK,
        )

        if clicked:
            if ig.is_mouse_double_clicked():
                editing.value = True
                initial_focus.value = False

        pressed = ig.is_item_hovered() and ig.is_mouse_clicked()

        return None, clicked, pressed

    cursor_pos = ig.get_cursor_pos()
    cursor_pos = (
        ig.get_window_position()[0] + cursor_pos[0],
        ig.get_window_position()[1] + cursor_pos[1] - ig.get_scroll_y(),
    )

    ig.push_item_width(size[0])
    value_text = dcast(str, formatter.output(value))
    buffer_size = len(value_text) + ig.get_clipboard_length() + 1000
    _, input = ig.input_text('##text-edit', value_text, buffer_size)
    ig.pop_item_width()

    if not initial_focus.value:
        ig.set_keyboard_focus_here(-1)
        initial_focus.value = True
    elif not ig.is_item_active():
        editing.value = False

    try:
        input_value = formatter.input(input)
        assert type(input_value) is type(value)
        if input_value != value:
            return Just(cast(T, input_value)), False, False
    except:
        # TODO: Show error message
        dl = ig.get_window_draw_list()
        dl.add_rect(
            cursor_pos[0],
            cursor_pos[1],
            cursor_pos[0] + size[0],
            cursor_pos[1] + ig.get_text_line_height() +
            2 * ig.get_style().frame_padding[1],
            ig.get_color_u32_rgba(1, 0, 0, 1),
        )

    return None, False, False
def render_joystick_control(
    id: str,
    stick_x: float,
    stick_y: float,
    shape='square',
) -> Optional[Tuple[float, float]]:
    ig.push_id(id)
    state = use_state('', JoystickControlState()).value

    dl = ig.get_window_draw_list()

    padding = 10
    content_region = ig.get_content_region_available()
    size = min(
        content_region.x - ig.get_style().scrollbar_size - 2 * padding,
        content_region.y - 2 * padding,
        200,
    )
    size = max(size, 100)

    initial_cursor_pos = ig.get_cursor_pos()
    top_left = (
        initial_cursor_pos[0] + ig.get_window_position()[0] -
        ig.get_scroll_x() + padding,
        initial_cursor_pos[1] + ig.get_window_position()[1] -
        ig.get_scroll_y() + padding,
    )

    background_color = ig.get_color_u32_rgba(0, 0, 0, 0.3)
    if shape == 'square':
        dl.add_rect_filled(
            top_left[0],
            top_left[1],
            top_left[0] + size,
            top_left[1] + size,
            background_color,
        )
    elif shape == 'circle':
        dl.add_circle_filled(
            top_left[0] + size / 2,
            top_left[1] + size / 2,
            size / 2,
            background_color,
            num_segments=32,
        )

    result = None

    if state.active and ig.is_mouse_down():
        new_offset = state.get_value(ig.get_mouse_drag_delta(lock_threshold=0))

        new_stick_x = new_offset[0] / size * 2 - 1
        new_stick_y = (1 - new_offset[1] / size) * 2 - 1
        if shape == 'square':
            new_stick_x = min(max(new_stick_x, -1), 1)
            new_stick_y = min(max(new_stick_y, -1), 1)
        elif shape == 'circle':
            mag = math.sqrt(new_stick_x**2 + new_stick_y**2)
            if mag > 1:
                new_stick_x /= mag
                new_stick_y /= mag

        if (new_stick_x, new_stick_y) != (stick_x, stick_y):
            stick_x, stick_y = new_stick_x, new_stick_y
            result = (stick_x, stick_y)

    offset = (
        (stick_x + 1) / 2 * size,
        (1 - (stick_y + 1) / 2) * size,
    )

    dl.add_line(
        top_left[0] + size / 2,
        top_left[1] + size / 2,
        top_left[0] + offset[0],
        top_left[1] + offset[1],
        ig.get_color_u32_rgba(1, 1, 1, 0.5),
    )

    button_size = 20
    button_pos = (
        padding + initial_cursor_pos[0] + offset[0] - button_size / 2,
        padding + initial_cursor_pos[1] + offset[1] - button_size / 2,
    )
    ig.set_cursor_pos(button_pos)
    ig.button('##joystick-button', button_size, button_size)

    ig.set_cursor_pos((
        initial_cursor_pos[0],
        initial_cursor_pos[1] + size + 2 * padding,
    ))

    if ig.is_item_active():
        state.set_active(offset)
    else:
        state.reset()

    ig.pop_id()
    return result
Exemple #12
0
    def render_rows(self) -> None:
        ig.columns(len(self.columns) + 1)

        min_row = int(ig.get_scroll_y() +
                      self.scroll_delta) // self.row_height - 1
        min_row = max(min_row, 0)
        max_row = int(ig.get_scroll_y() + self.scroll_delta +
                      ig.get_window_height()) // self.row_height
        # max_row = min(max_row, self.get_row_count() - 1)

        self.sequence.extend_to_frame(max_row + 100)

        timeline_operations: List[Callable[[], None]] = []

        mouse_pos = (
            ig.get_mouse_pos().x - ig.get_window_position().x,
            ig.get_mouse_pos().y - ig.get_window_position().y +
            ig.get_scroll_y() + self.scroll_delta,
        )

        for row in range(min_row, max_row + 1):
            row_pos = (0.0, row * self.row_height - self.scroll_delta)
            ig.set_cursor_pos(row_pos)

            mouse_in_row = mouse_pos[1] > row_pos[1] and mouse_pos[
                1] <= row_pos[1] + self.row_height
            if mouse_in_row and self.dragging and time.time(
            ) - self.time_started_dragging > 0.1:
                self.drag_handler.update_drag(row)

            if len(self.columns) > 0:
                ig.set_column_width(-1, self.frame_column_width)
            clicked, _ = ig.selectable(
                str(row) + '##fs-framenum-' + str(id(self)) + '-' + str(row),
                row == self.sequence.selected_frame,
                height=self.row_height - 8,  # TODO: Compute padding
            )
            if clicked:
                self.sequence.set_selected_frame(row)

            if ig.begin_popup_context_item('##fs-framenumctx-' +
                                           str(id(self)) + '-' + str(row)):
                if ig.selectable('Insert above')[0]:

                    def op(row):
                        return lambda: self.sequence.insert_frame(row)

                    timeline_operations.append(op(row))
                if ig.selectable('Insert below')[0]:

                    def op(row):
                        return lambda: self.sequence.insert_frame(row + 1)

                    timeline_operations.append(op(row))
                if ig.selectable('Delete')[0]:

                    def op(row):
                        return lambda: self.sequence.delete_frame(row)

                    timeline_operations.append(op(row))
                ig.end_popup_context_item()

            ig.next_column()

            for column in self.columns:
                self.render_cell(row, column)

                ig.set_column_width(-1, column.width)
                ig.next_column()
            ig.separator()

        ig.set_cursor_pos((0, (self.sequence.max_frame + 1) * self.row_height))

        ig.columns(1)

        for operation in timeline_operations:
            operation()
Exemple #13
0
def render_tabs(
    id: str,
    tabs: List[TabInfo],
    open_tab_index: Optional[int] = None,
    allow_windowing=False,
) -> Tuple[Optional[int], Optional[int]]:
    ig.push_id(id)
    root_id = get_local_state_id_stack()
    ig.columns(2)

    closed_tab = None

    rendered = use_state('rendered', False)
    if not rendered.value:
        rendered.value = True
        ig.set_column_width(-1, 120)

    if len(tabs) == 0:
        ig.pop_id()
        return None, closed_tab

    selected_tab_index = use_state_with('selected-tab-index',
                                        lambda: open_tab_index or 0)
    selected_tab_id = use_state_with('selected-tab',
                                     lambda: tabs[selected_tab_index.value].id)

    if open_tab_index is not None:
        selected_tab_index.value = open_tab_index
        selected_tab_id.value = tabs[open_tab_index].id

    windowed_tabs = use_state('windowed-tabs', cast(Set[str], set())).value

    # TODO: Change selected tab if windowed

    # Handle deletion/insertion
    if selected_tab_index.value >= len(tabs):
        selected_tab_index.value = len(tabs) - 1
    if tabs[selected_tab_index.value].id != selected_tab_id.value:
        matching_indices = [
            i for i in range(len(tabs)) if tabs[i].id == selected_tab_id.value
        ]
        if len(matching_indices) > 0:
            selected_tab_index.value = matching_indices[0]
        else:
            selected_tab_id.value = tabs[selected_tab_index.value].id

    ig.begin_child('tabs')
    for i, tab in enumerate(tabs):
        if tab.id in windowed_tabs:
            continue

        _, selected = ig.selectable(
            tab.label + '##tab-' + tab.id,
            selected_tab_id.value == tab.id,
        )
        if selected:
            selected_tab_index.value = i
            selected_tab_id.value = tab.id

        if tab.closable and ig.is_item_hovered() and ig.is_mouse_clicked(2):
            closed_tab = i

        if allow_windowing or tab.closable:
            if ig.begin_popup_context_item(f'##ctx-{tab.id}'):
                if allow_windowing and ig.selectable('Pop out')[0]:
                    windowed_tabs.add(tab.id)
                if tab.closable and ig.selectable('Close')[0]:
                    closed_tab = i
                ig.end_popup_context_item()

    ig.end_child()

    ig.next_column()

    ig.begin_child('content', flags=ig.WINDOW_HORIZONTAL_SCROLLING_BAR)
    tab = tabs[selected_tab_index.value]
    if tab.id not in windowed_tabs:
        push_local_state_rebase(('rebase-tabs', ) + root_id)
        tab.render(tab.id)  # type: ignore
        pop_local_state_rebase()
    ig.end_child()

    ig.columns(1)

    for tab_id in set(windowed_tabs):
        matching = [tab for tab in tabs if tab.id == tab_id]
        if len(matching) == 0:
            windowed_tabs.remove(tab.id)
            continue
        tab = matching[0]

        ig.set_next_window_size(*ig.get_window_size(), ig.ONCE)
        ig.set_next_window_position(*ig.get_window_position(), ig.ONCE)

        ig.push_style_color(ig.COLOR_WINDOW_BACKGROUND, 0.06, 0.06, 0.06, 0.94)
        _, opened = ig.begin(
            tab.label + '##window-' + tab.id,
            closable=True,
            flags=ig.WINDOW_HORIZONTAL_SCROLLING_BAR,
        )
        push_local_state_rebase(('rebase-tabs', ) + root_id)
        tab.render(tab.id)  # type: ignore
        pop_local_state_rebase()
        ig.end()
        ig.pop_style_color()

        if not opened:
            windowed_tabs.remove(tab.id)

    ig.pop_id()
    return (
        None if open_tab_index == selected_tab_index.value else
        selected_tab_index.value,
        closed_tab,
    )