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
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
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
def _render_window( render: Callable[[str], None]) -> Tuple[object, List[core.Scene]]: global first_render # TODO: clipboard length ig.set_clipboard_length(0) style = ig.get_style() style.window_rounding = 0 ig.get_style().colors[ig.COLOR_WINDOW_BACKGROUND] = (0, 0, 0, 0) ig.new_frame() ig.set_next_window_position(0, 0) ig.set_next_window_size(*ig.get_io().display_size) ig.begin( 'Main', False, ig.WINDOW_NO_SAVED_SETTINGS | ig.WINDOW_NO_RESIZE | ig.WINDOW_NO_TITLE_BAR | ig.WINDOW_MENU_BAR | ig.WINDOW_NO_BRING_TO_FRONT_ON_FOCUS, ) if first_render: # First render should be quick to avoid showing garbage for too long first_render = False else: render('root') ig.end() ig.end_frame() ig.render() draw_data = ig.get_draw_data() # ig_renderer.render(draw_data) return draw_data, graphics.take_scenes()
def render_value(label: str, value: object, formatter: VariableFormatter) -> Optional[Any]: label_width = 60 value_size = ( 60 if label == 'dyaw' else 80, ig.get_text_line_height() + 2 * ig.get_style().frame_padding[1], ) ig.push_item_width(label_width) ig.selectable(label, width=label_width) ig.pop_item_width() ig.same_line() new_value, _, _ = ui.render_variable_value('value-' + label, value, formatter, value_size) return None if new_value is None else new_value.value
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
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