cells_init :: (tree_copy: []int, stat: *Statistics) { row_count, col_count = dimensions(tree_copy.count); create_cells(tree_copy, stat); // Note: if we do hot reloading, we have to manage memory here! this_allocation_is_not_a_leak(cells.data); } cells_fit_zoom :: () -> Camera2D { width := col_count * CELL_WIDTH_HEIGHT; height := row_count * CELL_WIDTH_HEIGHT; center := Vector2.{ width / 2.0, height / 2.0 }; zoom_x := (SCREEN_WIDTH * 0.7) / width; zoom_y := (SCREEN_HEIGHT * 0.7) / height; fit_zoom := ifx zoom_x < zoom_y then zoom_x else zoom_y; cam := camera; cam.zoom = fit_zoom; cam.offset = { SCREEN_WIDTH / 2.0, SCREEN_HEIGHT / 2.0 }; cam.target = center; camera = cam; return cam; } cells_draw_2d :: () { assert(cells.count > 0, "Cell array empty"); first_cell := cells[0].rect; last_cell := cells[cells.count-1].rect; last_cell_in_first_row := cells[col_count].rect; whole_cell: Rectangle; { using whole_cell; x = first_cell.x; y = first_cell.y; width = (last_cell_in_first_row.x + last_cell_in_first_row.width) - first_cell.x; height = (last_cell.y + last_cell.width) - first_cell.y; } is_mouse_inside_any_cell := CheckCollisionPointRec(mouse_world, whole_cell); for cells { using it; new_color := color; inactive_color := color - { 0, 0, 0, 130 }; inactive_border := COLOR_CELLS_BORDER - { 0, 0, 0, 100 }; border_thickness := CELL_BORDER_THICK_NORMAL; is_mouseover := CheckCollisionPointRec(mouse_world, rect); if is_mouseover { new_color += { 30, 30, 30, 0 }; border_thickness = CELL_BORDER_THICK_ACTIVE; } if is_mouse_inside_any_cell { if is_mouseover { DrawRectangleRec(rect, new_color); DrawRectangleLinesEx(rect, border_thickness, COLOR_CELLS_BORDER_HIGHLIGHT); } else { DrawRectangleRec(rect, inactive_color); DrawRectangleLinesEx(rect, border_thickness, inactive_border); } } else { DrawRectangleRec(rect, new_color); DrawRectangleLinesEx(rect, border_thickness, COLOR_CELLS_BORDER); } } { parent :: #code { new_color := cell.color + COLOR_HIGHLIGHT0; DrawRectangleRec(cell.rect, new_color); } child_left :: #code { new_color := cell.color + COLOR_HIGHLIGHT0; DrawRectangleRec(cell.rect, new_color); } child_right :: #code { new_color := cell.color + COLOR_HIGHLIGHT0; DrawRectangleRec(cell.rect, new_color); } draw_hover_cells(parent, child_left, child_right); } { parent :: #code { border_color := cell.color + COLOR_HIGHLIGHT2; DrawRectangleLinesEx( cell.rect, CELL_BORDER_THICK_ACTIVE, border_color ); do_text( "P", { cell.rect.x, cell.rect.y }, CELL_INFO_FONT_SIZE, .SOUTH_WEST, border_color ); } child_left :: #code { border_color := cell.color + COLOR_HIGHLIGHT2; DrawRectangleLinesEx( cell.rect, CELL_BORDER_THICK_ACTIVE, border_color ); do_text( "L", { cell.rect.x, cell.rect.y }, CELL_INFO_FONT_SIZE, .SOUTH_WEST, border_color ); } child_right :: #code { border_color := cell.color + COLOR_HIGHLIGHT2; DrawRectangleLinesEx( cell.rect, CELL_BORDER_THICK_ACTIVE, border_color ); do_text( "R", { cell.rect.x, cell.rect.y }, CELL_INFO_FONT_SIZE, .SOUTH_WEST, border_color ); } draw_hover_cells(parent, child_left, child_right); } for cells { using it; do_text(label, { rect.x, rect.y }, 30, .CENTER, WHITE); do_text( tprint("%", it.index), { rect.x, rect.y }, 10, .NORTH_WEST, WHITE ); } } cells_draw_screen :: (stats: *Statistics) { gui_statistics(stats); if !flag_has(.TOOLTIP) return; for cells { if !it.label continue; if CheckCollisionPointRec(mouse_world, it.rect) { text := tooltip_text(it_index, it.value); tooltip(mouse_screen, text); } } } #scope_file cells: [..]Cell; row_count, col_count: int; CELL_INFO_FONT_SIZE :: 20; CELL_BORDER_THICK_NORMAL :: 1.0; CELL_BORDER_THICK_ACTIVE :: 2.0; CELL_WIDTH_HEIGHT :: 100.0; CELL_ASPECT_RATIO :float: #run 16.0 / 9.0; CELL_GAP :: 4.0; Cell :: struct { index: int; value: int; rect: Rectangle; label: string; color: Color; } /** Overkill, but maybe we need it */ Anchor :: enum { CENTER; NORTH; EAST; SOUTH; WEST; NORTH_EAST; NORTH_WEST; SOUTH_EAST; SOUTH_WEST; } dimensions :: (length: int) -> (row: int, col: int) { flen := cast(float, length); row := ceil(sqrt(flen / CELL_ASPECT_RATIO)); col := ceil(flen / row); return cast(int, row), cast(int, col); } cell_pos_from_index :: (index: int) -> Rectangle { col := index % col_count; row := index / col_count; return .{ x = col * (CELL_WIDTH_HEIGHT + CELL_GAP), y = row * (CELL_WIDTH_HEIGHT + CELL_GAP), width = CELL_WIDTH_HEIGHT, height = CELL_WIDTH_HEIGHT, }; } create_cells :: (tree: []int, stat: *Statistics) { CELL_WITH_GAP :: #run CELL_WIDTH_HEIGHT + CELL_GAP; rect: Rectangle; rect.width = CELL_WIDTH_HEIGHT; rect.height = CELL_WIDTH_HEIGHT; rect.x = -CELL_WITH_GAP; idx_col := col_count + 1; for tree { cell: Cell; if idx_col { rect.x += CELL_WITH_GAP; idx_col -= 1; } else { rect.x = 0.0; rect.y += CELL_WITH_GAP; idx_col = col_count; } if it_index == 0 { cell.label = "R"; cell.color = COLOR_NODE_ROOT; stat.nodes += 1; } else { if it == { case NODE_INTERNAL; cell.label = "N"; cell.color = COLOR_NODE_INTERNAL; stat.nodes += 1; case NODE_EMPTY; cell.color = COLOR_NODE_EMPTY; stat.empty += 1; case; cell.label = tprint("%", it); cell.color = COLOR_NODE_VALUE; stat.children += 1; } } cell.rect = rect; cell.index = it_index; cell.value = it; array_add(*cells, cell); } } draw_hover_cells :: ( $parent: Code, $child_left: Code, $child_right: Code, ) { for cells { using it; if CheckCollisionPointRec(mouse_world, rect) && label { parent_idx := cast(int)floor((it_index - 1) / 2.0); left_idx := 2 * it_index + 1; right_idx := 2 * it_index + 2; n := tree.count; if it_index > 0 && cells[parent_idx].label { cell := cells[parent_idx]; #insert,scope() parent; } if left_idx < n && cells[left_idx].label { cell := cells[left_idx]; #insert,scope() child_left; } if right_idx < n && cells[right_idx].label { cell := cells[right_idx]; #insert,scope() child_right; } } } } do_text :: (text: string, pos: Vector2, size: s32, anchor: Anchor, color: Color) { label_c := to_c_string(text); tw := MeasureTextEx(GetFontDefault(), label_c, xx size, 1); local_pos: Vector2; PADDING :: Vector2.{ 6.0, 4.0 }; if anchor == { case .CENTER; local_pos.x = pos.x + (CELL_WIDTH_HEIGHT / 2.0) - (tw.x / 2.0); local_pos.y = pos.y + (CELL_WIDTH_HEIGHT / 2.0) - (tw.y / 2.0); case .NORTH; local_pos = pos + { CELL_WIDTH_HEIGHT / 2.0 - (tw.x / 2.0), PADDING.y }; case .EAST; local_pos = pos + { CELL_WIDTH_HEIGHT - (tw.x + PADDING.x), CELL_WIDTH_HEIGHT / 2.0 - (tw.y / 2.0) }; case .SOUTH; local_pos = pos + { CELL_WIDTH_HEIGHT / 2.0 - (tw.x / 2.0), (CELL_WIDTH_HEIGHT - PADDING.y - tw.y) }; case .WEST; local_pos = pos + { PADDING.x, CELL_WIDTH_HEIGHT / 2.0 - (tw.y / 2.0) }; case .NORTH_EAST; local_pos = pos + { CELL_WIDTH_HEIGHT - PADDING.x - tw.x, PADDING.y }; case .NORTH_WEST; local_pos = pos + PADDING; case .SOUTH_EAST; local_pos = pos + { CELL_WIDTH_HEIGHT - PADDING.x - tw.x, CELL_WIDTH_HEIGHT - PADDING.y - tw.y }; case .SOUTH_WEST; local_pos = pos + { PADDING.x, CELL_WIDTH_HEIGHT - PADDING.y - tw.y }; } DrawText( label_c, xx local_pos.x, xx local_pos.y, size, color ); }