def render_input_tab(self, tab: TabId) -> None: column_sizes = [170, 370, 200] ig.set_next_window_content_size(sum(column_sizes), 0) ig.begin_child('##input', flags=ig.WINDOW_HORIZONTAL_SCROLLING_BAR) ig.columns(3) for i, w in enumerate(column_sizes): ig.set_column_width(i, w) def render_button(button: str) -> None: self.render_variable( tab, Variable('input-button-' + button).with_frame( self.model.selected_frame), 10, 25, ) ig.dummy(1, 3) render_button('a') ig.same_line() render_button('b') ig.same_line() render_button('z') ig.dummy(1, 5) render_button('s') ig.same_line() ig.dummy(43, 1) ig.same_line() render_button('r') ig.dummy(1, 5) ig.dummy(43, 1) ig.same_line() render_button('cu') ig.dummy(17, 1) ig.same_line() render_button('cl') ig.same_line() render_button('cr') ig.dummy(43, 1) ig.same_line() render_button('cd') ig.next_column() self.render_intended_stick_control('intended') ig.next_column() self.render_stick_control('joystick', tab) ig.columns(1) ig.end_child()
def render_right_column(self) -> None: total_height = ig.get_window_height() if self.show_debug_pane: ig.push_id('debug-pane') ig.begin_child('##pane', height=int(ig.get_window_height() * 0.15)) ig.columns(2) ig.set_column_width(-1, ig.get_window_width() - 300) ig.begin_child('##log') def init_log() -> List[str]: messages = [] log.subscribe(lambda msg: messages.append(str(msg))) return messages messages = use_state_with('messages', init_log).value prev_length = use_state('prev-length', 0) total_height -= ig.get_window_height() if prev_length.value != len(messages): prev_length.value = len(messages) ig.set_scroll_y(ig.get_scroll_max_y() + ig.get_window_height()) for message in messages: ig.text(message) ig.end_child() ig.next_column() for line in log.timer.format(log.timer.get_summaries()): ig.text(line) ig.columns(1) ig.end_child() ig.pop_id() log.timer.begin('fsheet') frame_sheet = self.frame_sheets[0] ig.set_next_window_content_size(frame_sheet.get_content_width(), 0) ig.begin_child( 'Frame Sheet##' + str(epoch) + '-0', height=int(total_height * 0.7), flags=ig.WINDOW_HORIZONTAL_SCROLLING_BAR, ) frame_sheet.render() ig.end_child() if ig.begin_drag_drop_target(): payload = ig.accept_drag_drop_payload('ve-var') if payload is not None: frame_sheet.append_variable(Variable.from_bytes(payload)) ig.end_drag_drop_target() log.timer.end() log.timer.begin('varexp') ig.begin_child('Variable Explorer', border=True) self.variable_explorer.render('variable-explorer') ig.end_child() log.timer.end()
def render(self) -> None: self.render_headers() # TODO: Make the vertical scrollbar always visible? ig.begin_child('Frame Sheet Rows', flags=ig.WINDOW_ALWAYS_VERTICAL_SCROLLBAR) self.update_scolling() min_frame = int(ig.get_scroll_y()) // self.row_height - 1 self.sequence.set_hotspot('frame-sheet-min', max(min_frame, 0)) if self.dragging and not ig.is_mouse_down(): self.drag_handler.release_drag() self.dragging = False self.render_rows() ig.end_child() self.columns = list(self.next_columns)
def render_left_column(self, framebuffer_size: Tuple[int, int]) -> None: total_height = ig.get_window_height() - ig.get_frame_height() # subtract menu bar slider_space = 45 wall_hitbox_radius = use_state('wall-hitbox-radius', 50) wall_hitbox_options = [0, 24, 50, 110] hovered_surface: Ref[Optional[int]] = use_state('hovered-surface', None) new_hovered_surface: Optional[int] = None hidden_surfaces_by_area = \ use_state('hidden-surfaces', cast(Dict[Tuple[int, int], Set[int]], {})).value current_area = ( dcast(int, self.model.get(Variable('level-num').with_frame(self.model.selected_frame))), dcast(int, self.model.get(Variable('area-index').with_frame(self.model.selected_frame))), ) hidden_surfaces = hidden_surfaces_by_area.setdefault(current_area, set()) log.timer.begin('gview1') ig.begin_child( 'Game View 1', height=int(total_height // 2) - slider_space // 2, border=True, ) hovered_surface_1 = ui.render_game_view_rotate( 'game-view-1', framebuffer_size, self.model, wall_hitbox_radius.value, hovered_surface.value, hidden_surfaces, ) ig.set_cursor_pos((10.0, ig.get_window_height() - 30)) ig.text('wall radius') ig.same_line() ig.push_item_width(50) _, index = ig.combo( '##wall-hitbox-radius', wall_hitbox_options.index(wall_hitbox_radius.value), list(map(str, wall_hitbox_options)), ) wall_hitbox_radius.value = wall_hitbox_options[index] ig.pop_item_width() ig.end_child() log.timer.end() log.timer.begin('gview2') ig.begin_child( 'Game View 2', height=int(total_height // 2) - slider_space // 2, border=True, ) hovered_surface_2 = ui.render_game_view_birds_eye( 'game-view-2', framebuffer_size, self.model, wall_hitbox_radius.value, hovered_surface.value, hidden_surfaces, ) ig.end_child() log.timer.end() new_hovered_surface = hovered_surface_1 or hovered_surface_2 if new_hovered_surface is not None and ig.is_mouse_clicked(1): ig.open_popup('surface-ctx') hovered_surface.value = new_hovered_surface if ig.begin_popup('surface-ctx'): if hovered_surface.value is not None: if hovered_surface.value in hidden_surfaces: if ig.menu_item('Show')[0]: hidden_surfaces.remove(hovered_surface.value) else: if ig.menu_item('Hide')[0]: hidden_surfaces.add(hovered_surface.value) if ig.menu_item('Properties')[0]: self.variable_explorer.open_surface_tab(hovered_surface.value) ig.end_popup() else: hovered_surface.value = new_hovered_surface if hovered_surface.value is not None and ig.is_mouse_clicked(2): if hovered_surface.value in hidden_surfaces: hidden_surfaces.remove(hovered_surface.value) else: hidden_surfaces.add(hovered_surface.value) speed_options = [0.05, 0.25, 0.5, 1, 2, 4] saved_play_direction = use_state('saved-play-direction', 0) saved_speed_index = use_state('saved-speed-index', 3) play_direction = saved_play_direction.value speed_index = saved_speed_index.value if play_direction == 0: frame_advance = 0 play_override = 0 def control(name: str, speed: int) -> None: nonlocal frame_advance, play_override x = input_down_gradual(name, 0.25) if x == 1.0: play_override = speed elif input_pressed(name): frame_advance += speed control('frame-next', 1) control('frame-next-alt', 1) control('frame-prev', -1) control('frame-prev-alt', -1) control('frame-next-fast', 10) control('frame-prev-fast', -10) if play_override != 0: if abs(play_override) in speed_options: speed_index = speed_options.index(abs(play_override)) else: speed_index = len(speed_options) - 1 play_direction = 1 if play_override > 0 else -1 else: self.model.selected_frame += frame_advance else: if input_down('frame-next') or input_down('frame-next-alt'): if play_direction == 1: speed_index += 1 else: play_direction = -play_direction elif input_down('frame-prev') or input_down('frame-prev-alt'): if play_direction == -1: speed_index += 1 else: play_direction = -play_direction elif input_down('frame-next-fast'): if play_direction == 1: speed_index += 2 else: play_direction = -play_direction speed_index += 1 elif input_down('frame-prev-fast'): if play_direction == -1: speed_index += 2 else: play_direction = -play_direction speed_index += 1 speed_index = min(max(speed_index, 0), len(speed_options) - 1) self.model.play_speed = play_direction * speed_options[speed_index] self.model.playback_mode = saved_play_direction.value != 0 def play_button(label: str, direction: int) -> None: disabled = play_direction == direction if ig.disableable_button(label, enabled=play_direction != direction): saved_play_direction.value = direction play_button('<|', -1) ig.same_line() play_button('||', 0) ig.same_line() play_button('|>', 1) ig.same_line() ig.push_item_width(63) changed, new_index = ig.combo( '##speed-option', speed_index, [str(s) + 'x' for s in speed_options], ) ig.pop_item_width() if changed: saved_speed_index.value = new_index if input_pressed('playback-play'): if saved_play_direction.value == 0: saved_play_direction.value = 1 else: saved_play_direction.value = 0 if input_pressed('playback-rewind'): if saved_play_direction.value == 0: saved_play_direction.value = -1 else: saved_play_direction.value = 0 if input_pressed('playback-speed-up'): saved_speed_index.value = min(saved_speed_index.value + 1, len(speed_options) - 1) if input_pressed('playback-slow-down'): saved_speed_index.value = max(saved_speed_index.value - 1, 0) ig.same_line() new_frame = ui.render_frame_slider( 'frame-slider', self.model.selected_frame, self.model.max_frame - 1, self.model.pipeline.cached_frames() if self.show_debug_pane else [], ) if new_frame is not None: self.model.selected_frame = new_frame.value
def render_game_view_birds_eye( id: str, framebuffer_size: Tuple[int, int], model: Model, wall_hitbox_radius: float, hovered_surface: Optional[int], hidden_surfaces: Set[int], ) -> Optional[int]: ig.push_id(id) # TODO: Should zoom in on mouse when uncentered mouse_state = use_state('mouse-state', MouseTracker()).value zoom = use_state('zoom', -4.5) target: Ref[Optional[Tuple[float, float]]] = use_state('target', None) pos_y: Ref[Optional[float]] = use_state('pos-y', None) drag_amount = mouse_state.get_drag_amount() zoom.value += mouse_state.get_wheel_amount() / 5 world_span_x = 200 / math.pow(2, zoom.value) viewport = get_viewport(framebuffer_size) mario_pos = get_mario_pos(model) # Camera xz camera_xz = (mario_pos[0], mario_pos[2]) if target.value is not None: camera_xz = target.value if drag_amount != (0.0, 0.0): world_span_z = world_span_x * viewport.width / viewport.height if target.value is None: target.value = (mario_pos[0], mario_pos[2]) target.value = ( camera_xz[0] + drag_amount[1] * world_span_x / viewport.height, camera_xz[1] - drag_amount[0] * world_span_z / viewport.width, ) camera_xz = target.value if ig.disableable_button('Lock to Mario', enabled=target.value is not None): target.value = None # Camera y camera_y = mario_pos[1] + 500 if pos_y.value is None else pos_y.value ig.set_cursor_pos((viewport.width - 100, 10)) ig.begin_child('##y-slider') new_y, reset = render_pos_y_slider('y-slider', camera_y, mario_pos[1]) if reset: pos_y.value = None elif new_y is not None: pos_y.value = new_y camera_y = pos_y.value ig.end_child() camera = core.BirdsEyeCamera() camera.pos = (camera_xz[0], camera_y, camera_xz[1]) camera.span_y = world_span_x # Mouse xz mouse_world_pos = get_mouse_world_pos_birds_eye(camera) mouse_ray: Optional[Tuple[Vec3f, Vec3f]] if mouse_world_pos is not None: ig.set_cursor_pos((10, viewport.height - 25)) ig.text('(x, z) = (%.3f, %.3f)' % mouse_world_pos) mouse_ray = ((mouse_world_pos[0], camera.pos[1], mouse_world_pos[1]), (0, -1, 0)) else: mouse_ray = None if mouse_ray is None: new_hovered_surface = None else: new_hovered_surface = trace_ray(model, mouse_ray) render_game( model, viewport, camera, False, wall_hitbox_radius, hovered_surface=hovered_surface, hidden_surfaces=hidden_surfaces, ) ig.pop_id() return new_hovered_surface
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, )