coordinate space math fixes, got resize handles working in settings menu prototype
will eventually lift to its own generic widget I still need to implement the corner resize..
This commit is contained in:
parent
b8e8e7c88a
commit
a2b6325b5b
@ -22,6 +22,7 @@ The dependencies are:
|
||||
* An ini parser
|
||||
|
||||
The client(sectr) module's organization is relatively flat due to the nature of odin's package management not allowing for cyclic dependencies across modules, and modules can only be in one directory.
|
||||
This makes it difficult to unflatten as the depedency chain must clear with no inter-module collisions, not something organic todo in a prototype...
|
||||
|
||||
Even so the notatble groups are:
|
||||
|
||||
|
@ -4,20 +4,24 @@ import rl "vendor:raylib"
|
||||
|
||||
Color :: rl.Color
|
||||
Color_Blue :: rl.BLUE
|
||||
Color_Green :: rl.GREEN
|
||||
// Color_Green :: rl.GREEN
|
||||
Color_Red :: rl.RED
|
||||
Color_White :: rl.WHITE
|
||||
|
||||
Color_Transparent :: Color { 0, 0, 0, 0 }
|
||||
Color_BG :: Color { 41, 41, 45, 255 }
|
||||
Color_BG_TextBox :: Color { 32, 32, 32, 180 }
|
||||
Color_BG_TextBox_Green :: Color { 102, 102, 110, 255 }
|
||||
Color_Frame_Disabled :: Color { 22, 22, 22, 120 }
|
||||
Color_Frame_Hover :: Color { 122, 122, 125, 200 }
|
||||
Color_Frame_Select :: Color { 188, 188, 188, 220 }
|
||||
Color_GreyRed :: Color { 220, 100, 100, 50 }
|
||||
Color_White_A125 :: Color { 255, 255, 255, 165 }
|
||||
Color_Black :: Color { 0, 0, 0, 255 }
|
||||
Color_Transparent :: Color { 0, 0, 0, 0 }
|
||||
Color_BG :: Color { 61, 61, 64, 255 }
|
||||
Color_BG_TextBox :: Color { 32, 32, 32, 180 }
|
||||
Color_BG_Panel :: Color { 32, 32, 32, 255 }
|
||||
Color_BG_Panel_Translucent :: Color { 32, 32, 32, 220 }
|
||||
Color_BG_TextBox_Green :: Color { 102, 102, 110, 255 }
|
||||
Color_Frame_Disabled :: Color { 22, 22, 22, 120 }
|
||||
Color_Frame_Hover :: Color { 122, 122, 125, 200 }
|
||||
Color_Frame_Select :: Color { 188, 188, 188, 220 }
|
||||
Color_GreyRed :: Color { 220, 100, 100, 50 }
|
||||
Color_White_A125 :: Color { 255, 255, 255, 165 }
|
||||
Color_Black :: Color { 0, 0, 0, 255 }
|
||||
Color_Green :: Color { 0, 180, 0, 255 }
|
||||
Color_ResizeHandle :: Color { 90, 90, 100, 255 }
|
||||
|
||||
Color_3D_BG :: Color { 188, 182 , 170, 255 }
|
||||
|
||||
|
@ -202,6 +202,9 @@ State :: struct {
|
||||
// the screen-space UI and the current workspace UI.
|
||||
// This is used so that the ui api doesn't need to have the user pass the context every single time.
|
||||
ui_context : ^UI_State,
|
||||
|
||||
// The camera is considered the "context" for coodrinate space operations in rendering
|
||||
cam_context : Camera,
|
||||
}
|
||||
|
||||
get_state :: proc "contextless" () -> ^ State {
|
||||
|
@ -134,9 +134,9 @@ dot :: proc {
|
||||
dot_unitv3_vs,
|
||||
}
|
||||
|
||||
draw_text :: proc {
|
||||
draw_text_string,
|
||||
draw_text_string_cached,
|
||||
ws_view_draw_text :: proc {
|
||||
ws_view_draw_text_string,
|
||||
ws_view_draw_text_StrRunesPair,
|
||||
}
|
||||
|
||||
from_bytes :: proc {
|
||||
|
@ -1,8 +1,15 @@
|
||||
/*
|
||||
This was a tracking allocator made to kill off various bugs left with grime's pool & slab allocators
|
||||
It doesn't perform that well on a per-frame basis and should be avoided for general memory debugging
|
||||
|
||||
It only makes sure that memory allocations don't collide in the allocator and deallocations don't occur for memory never allocated.
|
||||
|
||||
I'm keeping it around as an artifact & for future allocators I may make.
|
||||
*/
|
||||
package sectr
|
||||
|
||||
MemoryTrackerEntry :: struct {
|
||||
start, end : rawptr,
|
||||
// owner : string,
|
||||
}
|
||||
|
||||
MemoryTracker :: struct {
|
||||
|
@ -269,14 +269,14 @@ MouseState :: struct {
|
||||
side, forward, back, extra : DigitalBtn
|
||||
}
|
||||
},
|
||||
pos, delta : Vec2,
|
||||
raw_pos, pos, delta : Vec2,
|
||||
vertical_wheel, horizontal_wheel : AnalogAxis
|
||||
}
|
||||
|
||||
mouse_world_delta :: #force_inline proc "contextless" () -> Vec2 {
|
||||
using state := get_state()
|
||||
cam := & state.project.workspace.cam
|
||||
return { input.mouse.delta.x, -input.mouse.delta.y } * ( 1 / cam.zoom )
|
||||
return input.mouse.delta * ( 1 / cam.zoom )
|
||||
}
|
||||
|
||||
InputState :: struct {
|
||||
@ -347,8 +347,9 @@ poll_input :: proc( old, new : ^ InputState )
|
||||
input_process_digital_btn( old_btn, new_btn, is_down )
|
||||
}
|
||||
|
||||
new.mouse.pos = rl.GetMousePosition() - transmute(Vec2) get_state().app_window.extent
|
||||
new.mouse.delta = rl.GetMouseDelta()
|
||||
new.mouse.raw_pos = rl.GetMousePosition()
|
||||
new.mouse.pos = render_to_surface_pos(new.mouse.raw_pos)
|
||||
new.mouse.delta = rl.GetMouseDelta() * {1, -1}
|
||||
new.mouse.vertical_wheel = rl.GetMouseWheelMove()
|
||||
}
|
||||
}
|
||||
|
@ -140,9 +140,9 @@ screen_size :: proc "contextless" () -> AreaSize {
|
||||
|
||||
screen_get_bounds :: #force_inline proc "contextless" () -> Range2 {
|
||||
state := get_state(); using state
|
||||
screen_extent := state.app_window.extent
|
||||
bottom_left := Vec2 { -screen_extent.x, -screen_extent.y}
|
||||
top_right := Vec2 { screen_extent.x, screen_extent.y}
|
||||
surface_extent := state.app_window.extent
|
||||
bottom_left := Vec2 { -surface_extent.x, -surface_extent.y}
|
||||
top_right := Vec2 { surface_extent.x, surface_extent.y}
|
||||
return range2( bottom_left, top_right )
|
||||
}
|
||||
|
||||
@ -156,6 +156,7 @@ screen_get_corners :: #force_inline proc "contextless"() -> BoundsCorners2 {
|
||||
return { top_left, top_right, bottom_left, bottom_right }
|
||||
}
|
||||
|
||||
// TODO(Ed): Use a cam/workspace context instead (when multiple workspaces viewproting supported)
|
||||
view_get_bounds :: #force_inline proc "contextless"() -> Range2 {
|
||||
state := get_state(); using state
|
||||
cam := & project.workspace.cam
|
||||
@ -166,6 +167,7 @@ view_get_bounds :: #force_inline proc "contextless"() -> Range2 {
|
||||
return range2( bottom_left, top_right )
|
||||
}
|
||||
|
||||
// TODO(Ed): Use a cam/workspace context instead (when multiple workspace viewproting)
|
||||
view_get_corners :: #force_inline proc "contextless"() -> BoundsCorners2 {
|
||||
state := get_state(); using state
|
||||
cam := & project.workspace.cam
|
||||
@ -178,30 +180,64 @@ view_get_corners :: #force_inline proc "contextless"() -> BoundsCorners2 {
|
||||
return { top_left, top_right, bottom_left, bottom_right }
|
||||
}
|
||||
|
||||
screen_to_world :: #force_inline proc "contextless" (pos: Vec2) -> Vec2 {
|
||||
state := get_state(); using state
|
||||
cam := & project.workspace.cam
|
||||
result := Vec2 { cam.target.x, -cam.target.y} + Vec2 { pos.x, -pos.y } * (1 / cam.zoom)
|
||||
render_to_surface_pos :: #force_inline proc "contextless" (pos : Vec2) -> Vec2 {
|
||||
extent := & get_state().app_window.extent
|
||||
result := Vec2 {
|
||||
pos.x - extent.x,
|
||||
pos.y * -1 + extent.y
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
screen_to_render :: #force_inline proc "contextless"(pos: Vec2) -> Vec2 {
|
||||
screen_extent := transmute(Vec2) get_state().project.workspace.cam.offset
|
||||
return pos + { screen_extent.x, -screen_extent.y }
|
||||
render_to_ws_view_pos :: #force_inline proc "contextless" (pos : Vec2) -> Vec2 {
|
||||
return {}
|
||||
}
|
||||
|
||||
world_screen_extent :: #force_inline proc "contextless"() -> Extents2 {
|
||||
surface_to_ws_view_pos :: #force_inline proc "contextless" (pos: Vec2) -> Vec2 {
|
||||
state := get_state(); using state
|
||||
cam := & project.workspace.cam
|
||||
result := Vec2 { cam.target.x, -cam.target.y} + Vec2 { pos.x, pos.y } * (1 / cam.zoom)
|
||||
return result
|
||||
}
|
||||
|
||||
// (Surface) Centered screen space to conventional screen space used for rendering
|
||||
surface_to_render_pos :: #force_inline proc "contextless" (pos : Vec2) -> Vec2 {
|
||||
screen_extent := transmute(Vec2) get_state().app_window.extent
|
||||
return pos * {1, -1} + { screen_extent.x, screen_extent.y }
|
||||
}
|
||||
|
||||
// TODO(Ed): These should assume a cam_context or have the ability to provide it in params
|
||||
|
||||
// Extent of workspace view (currently hardcoded to the app window's extent, eventually will be based on a viewport object's extent field)
|
||||
// TODO(Ed): Support a position which would not be centered on the screen if in a viewport
|
||||
ws_view_extent :: #force_inline proc "contextless"() -> Extents2 {
|
||||
state := get_state(); using state
|
||||
cam_zoom_ratio := 1.0 / project.workspace.cam.zoom
|
||||
return app_window.extent * cam_zoom_ratio
|
||||
}
|
||||
|
||||
world_to_screen_pos :: #force_inline proc "contextless"(position: Vec2) -> Vec2 {
|
||||
// Workspace view to surface space position
|
||||
// TODO(Ed): Support a position which would not be centered on the screen if in a viewport
|
||||
ws_view_to_surface_pos :: #force_inline proc "contextless"(position: Vec2) -> Vec2 {
|
||||
return position
|
||||
}
|
||||
|
||||
ws_view_to_render_pos :: #force_inline proc "contextless"(position: Vec2) -> Vec2 {
|
||||
return { position.x, position.y * -1 }
|
||||
}
|
||||
|
||||
world_to_screen_no_zoom :: #force_inline proc "contextless"(position: Vec2) -> Vec2 {
|
||||
// Workspace view to surface space position (zoom agnostic)
|
||||
// TODO(Ed): Support a position which would not be centered on the screen if in a viewport
|
||||
ws_view_to_surface_pos_no_zoom :: #force_inline proc "contextless"(position: Vec2) -> Vec2 {
|
||||
state := get_state(); using state
|
||||
cam_zoom_ratio := 1.0 / state.project.workspace.cam.zoom
|
||||
return { position.x, position.y * -1 } * cam_zoom_ratio
|
||||
return { position.x, position.y } * cam_zoom_ratio
|
||||
}
|
||||
|
||||
// Workspace view to render space position (zoom agnostic)
|
||||
// TODO(Ed): Support a position which would not be centered on the screen if in a viewport
|
||||
ws_view_to_render_pos_no_zoom :: #force_inline proc "contextless"(position: Vec2) -> Vec2 {
|
||||
state := get_state(); using state
|
||||
cam_zoom_ratio := 1.0 / state.project.workspace.cam.zoom
|
||||
return { position.x, position.y } * cam_zoom_ratio
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ debug_draw_text :: proc( content : string, pos : Vec2, size : f32, color : rl.Co
|
||||
// if ( len(font) == 0 ) {
|
||||
font = default_font
|
||||
}
|
||||
pos := screen_to_render(pos)
|
||||
pos := surface_to_render_pos(pos)
|
||||
|
||||
px_size := size
|
||||
|
||||
@ -34,7 +34,34 @@ debug_draw_text :: proc( content : string, pos : Vec2, size : f32, color : rl.Co
|
||||
tint = color );
|
||||
}
|
||||
|
||||
draw_text_string :: proc( content : string, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
|
||||
draw_text_screenspace :: proc( content : StrRunesPair, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
|
||||
{
|
||||
// profile(#procedure)
|
||||
state := get_state(); using state
|
||||
|
||||
if len( content.str ) == 0 {
|
||||
return
|
||||
}
|
||||
font := font
|
||||
if font.key == Font_Default.key {
|
||||
font = default_font
|
||||
}
|
||||
pos := pos
|
||||
|
||||
rl_font := to_rl_Font(font, size )
|
||||
runes := content.runes
|
||||
|
||||
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.TRILINEAR)
|
||||
rl.DrawTextCodepoints( rl_font,
|
||||
raw_data(runes), cast(i32) len(runes),
|
||||
position = transmute(rl.Vector2) pos,
|
||||
fontSize = size,
|
||||
spacing = 0.0,
|
||||
tint = color );
|
||||
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.POINT)
|
||||
}
|
||||
|
||||
ws_view_draw_text_string :: proc( content : string, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
|
||||
{
|
||||
// profile(#procedure)
|
||||
state := get_state(); using state
|
||||
@ -50,7 +77,7 @@ draw_text_string :: proc( content : string, pos : Vec2, size : f32, color : rl.C
|
||||
// if len(font) == 0 {
|
||||
font = default_font
|
||||
}
|
||||
pos := world_to_screen_pos(pos)
|
||||
pos := ws_view_to_render_pos(pos)
|
||||
|
||||
px_size := size
|
||||
zoom_adjust := px_size * project.workspace.cam.zoom
|
||||
@ -66,7 +93,7 @@ draw_text_string :: proc( content : string, pos : Vec2, size : f32, color : rl.C
|
||||
rl.SetTextureFilter(rl_font.texture, rl.TextureFilter.POINT)
|
||||
}
|
||||
|
||||
draw_text_string_cached :: proc( content : StrRunesPair, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
|
||||
ws_view_draw_text_StrRunesPair :: proc( content : StrRunesPair, pos : Vec2, size : f32, color : rl.Color = rl.WHITE, font : FontID = Font_Default )
|
||||
{
|
||||
// profile(#procedure)
|
||||
state := get_state(); using state
|
||||
@ -78,7 +105,7 @@ draw_text_string_cached :: proc( content : StrRunesPair, pos : Vec2, size : f32,
|
||||
if font.key == Font_Default.key {
|
||||
font = default_font
|
||||
}
|
||||
pos := world_to_screen_pos(pos)
|
||||
pos := ws_view_to_render_pos(pos)
|
||||
|
||||
px_size := size
|
||||
zoom_adjust := px_size * project.workspace.cam.zoom
|
||||
|
@ -4,6 +4,23 @@ import "core:fmt"
|
||||
|
||||
import rl "vendor:raylib"
|
||||
|
||||
draw_rectangle :: #force_inline proc "contextless" ( rect : rl.Rectangle, style : UI_Style ) {
|
||||
if style.layout.corner_radii[0] > 0 {
|
||||
rl.DrawRectangleRounded( rect, style.layout.corner_radii[0], 9, style.bg_color )
|
||||
}
|
||||
else {
|
||||
rl.DrawRectangleRec( rect, style.bg_color )
|
||||
}
|
||||
}
|
||||
|
||||
draw_rectangle_lines :: #force_inline proc "contextless" ( rect : rl.Rectangle, style : UI_Style, color : Color, thickness : f32 ) {
|
||||
if style.layout.corner_radii[0] > 0 {
|
||||
rl.DrawRectangleRoundedLines( rect, style.layout.corner_radii[0], 9, thickness, color )
|
||||
}
|
||||
else {
|
||||
rl.DrawRectangleLinesEx( rect, thickness, color )
|
||||
}
|
||||
}
|
||||
|
||||
render :: proc()
|
||||
{
|
||||
@ -15,124 +32,34 @@ render :: proc()
|
||||
rl.BeginDrawing()
|
||||
rl.ClearBackground( Color_BG )
|
||||
|
||||
render_mode_2d()
|
||||
render_mode_2d_workspace()
|
||||
render_mode_screenspace()
|
||||
|
||||
rl.EndDrawing()
|
||||
}
|
||||
|
||||
render_mode_screenspace :: proc ()
|
||||
// Experimental 3d viewport, not really the focus of this prototype
|
||||
// Until we can have a native or interpreted program render to it its not very useful.
|
||||
// Note(Ed): Other usecase could be 3d vis notes & math/graphical debug
|
||||
render_mode_3d :: proc()
|
||||
{
|
||||
profile("Render Screenspace")
|
||||
profile(#procedure)
|
||||
|
||||
state := get_state(); using state
|
||||
replay := & Memory_App.replay
|
||||
cam := & project.workspace.cam
|
||||
win_extent := state.app_window.extent
|
||||
|
||||
//region App UI
|
||||
Render_App_UI:
|
||||
{
|
||||
profile("App UI")
|
||||
ui := & state.app_ui
|
||||
root := ui.root
|
||||
if root.num_children == 0 {
|
||||
break Render_App_UI
|
||||
}
|
||||
rl.BeginDrawing()
|
||||
rl.BeginTextureMode( debug.viewport_rt )
|
||||
rl.BeginMode3D( debug.cam_vp )
|
||||
rl.ClearBackground( Color_3D_BG )
|
||||
|
||||
current := root.first
|
||||
for ; current != nil; current = ui_box_tranverse_next( current )
|
||||
{
|
||||
// profile("Box")
|
||||
parent := current.parent
|
||||
|
||||
style := current.style
|
||||
computed := & current.computed
|
||||
|
||||
computed_size := computed.bounds.p1 - computed.bounds.p0
|
||||
}
|
||||
}
|
||||
//endregion App UI
|
||||
|
||||
screen_top_left : Vec2 = {
|
||||
-win_extent.x + cam.target.x,
|
||||
-win_extent.y + cam.target.y,
|
||||
}
|
||||
|
||||
fps_msg := str_fmt_tmp( "FPS: %f", fps_avg)
|
||||
fps_msg_width := measure_text_size( fps_msg, default_font, 16.0, 0.0 ).x
|
||||
fps_msg_pos := screen_get_corners().top_right - { fps_msg_width, 0 }
|
||||
debug_draw_text( fps_msg, fps_msg_pos, 16.0, color = rl.GREEN )
|
||||
|
||||
debug_text :: proc( format : string, args : ..any )
|
||||
{
|
||||
@static draw_text_scratch : [Kilobyte * 64]u8
|
||||
|
||||
state := get_state(); using state
|
||||
if debug.draw_debug_text_y > 800 {
|
||||
debug.draw_debug_text_y = 50
|
||||
}
|
||||
|
||||
cam := & project.workspace.cam
|
||||
screen_corners := screen_get_corners()
|
||||
|
||||
position := screen_corners.top_right
|
||||
position.x -= 800
|
||||
position.y += debug.draw_debug_text_y
|
||||
|
||||
content := str_fmt_buffer( draw_text_scratch[:], format, ..args )
|
||||
debug_draw_text( content, position, 14.0 )
|
||||
|
||||
debug.draw_debug_text_y += 14
|
||||
}
|
||||
|
||||
// Debug Text
|
||||
{
|
||||
// debug_text( "Screen Width : %v", rl.GetScreenWidth () )
|
||||
// debug_text( "Screen Height: %v", rl.GetScreenHeight() )
|
||||
debug_text( "frametime_target_ms : %f ms", frametime_target_ms )
|
||||
debug_text( "frametime : %f ms", frametime_delta_ms )
|
||||
// debug_text( "frametime_last_elapsed_ms : %f ms", frametime_elapsed_ms )
|
||||
if replay.mode == ReplayMode.Record {
|
||||
debug_text( "Recording Input")
|
||||
}
|
||||
if replay.mode == ReplayMode.Playback {
|
||||
debug_text( "Replaying Input")
|
||||
}
|
||||
}
|
||||
|
||||
debug_text("Zoom Target: %v", project.workspace.zoom_target)
|
||||
|
||||
if debug.mouse_vis {
|
||||
debug_text( "Mouse Vertical Wheel: %v", input.mouse.vertical_wheel )
|
||||
debug_text( "Mouse Position (Screen): %v", input.mouse.pos )
|
||||
debug_text("Mouse Position (World): %v", screen_to_world(input.mouse.pos) )
|
||||
cursor_pos := transmute(Vec2) state.app_window.extent + input.mouse.pos
|
||||
rl.DrawCircleV( cursor_pos, 10, Color_White_A125 )
|
||||
}
|
||||
|
||||
ui := & project.workspace.ui
|
||||
|
||||
// debug_text("Box Count: %v", ui.built_box_count )
|
||||
|
||||
hot_box := ui_box_from_key( ui.curr_cache, ui.hot )
|
||||
active_box := ui_box_from_key( ui.curr_cache, ui.active )
|
||||
if hot_box != nil {
|
||||
debug_text("Hot Box : %v", hot_box.label.str )
|
||||
// debug_text("Hot Range2: %v", hot_box.computed.bounds.pts)
|
||||
}
|
||||
if active_box != nil{
|
||||
// debug_text("Active Box: %v", active_box.label.str )
|
||||
}
|
||||
// debug_text("Active Resizing: %v", ui.active_start_signal.resizing)
|
||||
|
||||
view := view_get_bounds()
|
||||
// debug_text("View Bounds (World): %v", view.pts )
|
||||
|
||||
debug.draw_debug_text_y = 50
|
||||
rl.EndMode3D()
|
||||
rl.EndTextureMode()
|
||||
rl.EndDrawing()
|
||||
}
|
||||
|
||||
render_mode_2d :: proc()
|
||||
// TODO(Ed): Eventually this needs to become a 'viewport within a UI'
|
||||
// This would allow the user to have more than one workspace open at the same time
|
||||
render_mode_2d_workspace :: proc()
|
||||
{
|
||||
profile(#procedure)
|
||||
state := get_state(); using state
|
||||
@ -164,8 +91,8 @@ render_mode_2d :: proc()
|
||||
when false
|
||||
{
|
||||
render_view := Range2 { pts = {
|
||||
world_to_screen_pos(view_bounds.min),
|
||||
world_to_screen_pos(view_bounds.max),
|
||||
world_to_screen_pos( view_bounds.min),
|
||||
world_to_screen_pos( view_bounds.max),
|
||||
}}
|
||||
view_rect := rl.Rectangle {
|
||||
render_view.min.x,
|
||||
@ -205,24 +132,24 @@ render_mode_2d :: proc()
|
||||
|
||||
// profile_begin("Calculating Raylib rectangles")
|
||||
render_anchors := range2(
|
||||
world_to_screen_pos(computed.anchors.min),
|
||||
world_to_screen_pos(computed.anchors.max),
|
||||
ws_view_to_render_pos(computed.anchors.min),
|
||||
ws_view_to_render_pos(computed.anchors.max),
|
||||
)
|
||||
render_margins := range2(
|
||||
world_to_screen_pos(computed.margins.min),
|
||||
world_to_screen_pos(computed.margins.max),
|
||||
ws_view_to_render_pos(computed.margins.min),
|
||||
ws_view_to_render_pos(computed.margins.max),
|
||||
)
|
||||
render_bounds := range2(
|
||||
world_to_screen_pos(computed.bounds.min),
|
||||
world_to_screen_pos(computed.bounds.max),
|
||||
ws_view_to_render_pos(computed.bounds.min),
|
||||
ws_view_to_render_pos(computed.bounds.max),
|
||||
)
|
||||
render_padding := range2(
|
||||
world_to_screen_pos(computed.padding.min),
|
||||
world_to_screen_pos(computed.padding.max),
|
||||
ws_view_to_render_pos(computed.padding.min),
|
||||
ws_view_to_render_pos(computed.padding.max),
|
||||
)
|
||||
render_content := range2(
|
||||
world_to_screen_pos(computed.content.min),
|
||||
world_to_screen_pos(computed.content.max),
|
||||
ws_view_to_render_pos(computed.content.min),
|
||||
ws_view_to_render_pos(computed.content.max),
|
||||
)
|
||||
|
||||
rect_anchors := range2_to_rl_rect( render_anchors )
|
||||
@ -232,24 +159,6 @@ render_mode_2d :: proc()
|
||||
rect_content := range2_to_rl_rect( render_content )
|
||||
// profile_end()
|
||||
|
||||
draw_rectangle :: #force_inline proc "contextless" ( rect : rl.Rectangle, style : UI_Style ) {
|
||||
if style.layout.corner_radii[0] > 0 {
|
||||
rl.DrawRectangleRounded( rect, style.layout.corner_radii[0], 9, style.bg_color )
|
||||
}
|
||||
else {
|
||||
rl.DrawRectangleRec( rect, style.bg_color )
|
||||
}
|
||||
}
|
||||
|
||||
draw_rectangle_lines :: #force_inline proc "contextless" ( rect : rl.Rectangle, style : UI_Style, color : Color, thickness : f32 ) {
|
||||
if style.layout.corner_radii[0] > 0 {
|
||||
rl.DrawRectangleRoundedLines( rect, style.layout.corner_radii[0], 9, thickness, color )
|
||||
}
|
||||
else {
|
||||
rl.DrawRectangleLinesEx( rect, thickness, color )
|
||||
}
|
||||
}
|
||||
|
||||
// profile_begin("rl.DrawRectangleRounded( rect_bounds, style.layout.corner_radii[0], 9, style.bg_color )")
|
||||
if style.bg_color.a != 0
|
||||
{
|
||||
@ -281,8 +190,8 @@ render_mode_2d :: proc()
|
||||
{ -resize_percent_width.x, resize_percent_width.x }))
|
||||
|
||||
render_resize := range2(
|
||||
world_to_screen_pos(resize_border_non_range.min),
|
||||
world_to_screen_pos(resize_border_non_range.max),
|
||||
ws_view_to_render_pos(resize_border_non_range.min),
|
||||
ws_view_to_render_pos(resize_border_non_range.max),
|
||||
)
|
||||
rect_resize := rl.Rectangle {
|
||||
render_resize.min.x,
|
||||
@ -307,7 +216,7 @@ render_mode_2d :: proc()
|
||||
// profile_end()
|
||||
|
||||
if len(current.text.str) > 0 {
|
||||
draw_text( current.text, world_to_screen_pos(computed.text_pos), style.font_size, style.text_color )
|
||||
ws_view_draw_text( current.text, ws_view_to_render_pos(computed.text_pos * {1, -1}), style.font_size, style.text_color )
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -315,8 +224,8 @@ render_mode_2d :: proc()
|
||||
|
||||
|
||||
if debug.mouse_vis {
|
||||
cursor_world_pos := screen_to_world(input.mouse.pos)
|
||||
rl.DrawCircleV( world_to_screen_pos(cursor_world_pos), 5, Color_GreyRed )
|
||||
cursor_world_pos := surface_to_ws_view_pos(input.mouse.pos)
|
||||
rl.DrawCircleV( ws_view_to_render_pos(cursor_world_pos), 5, Color_GreyRed )
|
||||
}
|
||||
|
||||
rl.DrawCircleV( { 0, 0 }, 1 * cam_zoom_ratio, Color_White )
|
||||
@ -324,18 +233,208 @@ render_mode_2d :: proc()
|
||||
rl.EndMode2D()
|
||||
}
|
||||
|
||||
render_mode_3d :: proc()
|
||||
render_mode_screenspace :: proc ()
|
||||
{
|
||||
profile("Render Screenspace")
|
||||
|
||||
state := get_state(); using state
|
||||
replay := & Memory_App.replay
|
||||
cam := & project.workspace.cam
|
||||
win_extent := state.app_window.extent
|
||||
|
||||
render_app_ui()
|
||||
|
||||
fps_msg := str_fmt_tmp( "FPS: %f", fps_avg)
|
||||
fps_msg_width := measure_text_size( fps_msg, default_font, 16.0, 0.0 ).x
|
||||
fps_msg_pos := screen_get_corners().top_right - { fps_msg_width, 0 } - { 5, 5 }
|
||||
debug_draw_text( fps_msg, fps_msg_pos, 16.0, color = rl.GREEN )
|
||||
|
||||
debug_text :: proc( format : string, args : ..any )
|
||||
{
|
||||
@static draw_text_scratch : [Kilobyte * 64]u8
|
||||
|
||||
state := get_state(); using state
|
||||
if debug.draw_debug_text_y > 800 {
|
||||
debug.draw_debug_text_y = 0
|
||||
}
|
||||
|
||||
cam := & project.workspace.cam
|
||||
screen_corners := screen_get_corners()
|
||||
|
||||
position := screen_corners.top_right
|
||||
position.x -= app_window.extent.x
|
||||
position.y -= debug.draw_debug_text_y
|
||||
|
||||
content := str_fmt_buffer( draw_text_scratch[:], format, ..args )
|
||||
debug_draw_text( content, position, 14.0 )
|
||||
|
||||
debug.draw_debug_text_y += 14
|
||||
}
|
||||
|
||||
// Debug Text
|
||||
{
|
||||
// debug_text( "Screen Width : %v", rl.GetScreenWidth () )
|
||||
// debug_text( "Screen Height: %v", rl.GetScreenHeight() )
|
||||
debug_text( "frametime_target_ms : %f ms", frametime_target_ms )
|
||||
debug_text( "frametime : %f ms", frametime_delta_ms )
|
||||
// debug_text( "frametime_last_elapsed_ms : %f ms", frametime_elapsed_ms )
|
||||
if replay.mode == ReplayMode.Record {
|
||||
debug_text( "Recording Input")
|
||||
}
|
||||
if replay.mode == ReplayMode.Playback {
|
||||
debug_text( "Replaying Input")
|
||||
}
|
||||
}
|
||||
|
||||
debug_text("Zoom Target: %v", project.workspace.zoom_target)
|
||||
|
||||
if debug.mouse_vis {
|
||||
debug_text("Mouse Vertical Wheel: %v", input.mouse.vertical_wheel )
|
||||
debug_text("Mouse Position (Render) : %v", input.mouse.raw_pos )
|
||||
debug_text("Mouse Position (Surface) : %v", input.mouse.pos )
|
||||
debug_text("Mouse Position (Workspace View): %v", surface_to_ws_view_pos(input.mouse.pos) )
|
||||
rl.DrawCircleV( input.mouse.raw_pos, 10, Color_White_A125 )
|
||||
rl.DrawCircleV( surface_to_render_pos(input.mouse.pos), 2, Color_BG )
|
||||
}
|
||||
|
||||
ui := & project.workspace.ui
|
||||
|
||||
// debug_text("Box Count: %v", ui.built_box_count )
|
||||
|
||||
hot_box := ui_box_from_key( ui.curr_cache, ui.hot )
|
||||
active_box := ui_box_from_key( ui.curr_cache, ui.active )
|
||||
if hot_box != nil {
|
||||
debug_text("Hot Box : %v", hot_box.label.str )
|
||||
// debug_text("Hot Range2: %v", hot_box.computed.bounds.pts)
|
||||
}
|
||||
if active_box != nil{
|
||||
// debug_text("Active Box: %v", active_box.label.str )
|
||||
}
|
||||
// debug_text("Active Resizing: %v", ui.active_start_signal.resizing)
|
||||
|
||||
view := view_get_bounds()
|
||||
// debug_text("View Bounds (World): %v", view.pts )
|
||||
|
||||
debug.draw_debug_text_y = 14
|
||||
}
|
||||
|
||||
// A non-zoomable static-view for ui
|
||||
// Only a scalar factor may be applied to the size of widgets & fonts
|
||||
// 'Window tiled' panels reside here
|
||||
render_app_ui :: proc()
|
||||
{
|
||||
profile(#procedure)
|
||||
|
||||
state := get_state(); using state
|
||||
using state := get_state()
|
||||
|
||||
rl.BeginDrawing()
|
||||
rl.BeginTextureMode( debug.viewport_rt )
|
||||
rl.BeginMode3D( debug.cam_vp )
|
||||
rl.ClearBackground( Color_3D_BG )
|
||||
//region App UI
|
||||
Render_App_UI:
|
||||
{
|
||||
profile("App UI")
|
||||
ui := & state.app_ui
|
||||
root := ui.root
|
||||
if root.num_children == 0 {
|
||||
break Render_App_UI
|
||||
}
|
||||
|
||||
rl.EndMode3D()
|
||||
rl.EndTextureMode()
|
||||
rl.EndDrawing()
|
||||
current := root.first
|
||||
for ; current != nil; current = ui_box_tranverse_next( current )
|
||||
{
|
||||
// profile("Box")
|
||||
parent := current.parent
|
||||
|
||||
style := current.style
|
||||
computed := & current.computed
|
||||
|
||||
computed_size := computed.bounds.p1 - computed.bounds.p0
|
||||
|
||||
render_anchors := range2(
|
||||
surface_to_render_pos(computed.anchors.min),
|
||||
surface_to_render_pos(computed.anchors.max),
|
||||
)
|
||||
render_margins := range2(
|
||||
surface_to_render_pos(computed.margins.min),
|
||||
surface_to_render_pos(computed.margins.max),
|
||||
)
|
||||
render_bounds := range2(
|
||||
surface_to_render_pos(computed.bounds.min),
|
||||
surface_to_render_pos(computed.bounds.max),
|
||||
)
|
||||
render_padding := range2(
|
||||
surface_to_render_pos(computed.padding.min),
|
||||
surface_to_render_pos(computed.padding.max),
|
||||
)
|
||||
render_content := range2(
|
||||
surface_to_render_pos(computed.content.min),
|
||||
surface_to_render_pos(computed.content.max),
|
||||
)
|
||||
rect_anchors := range2_to_rl_rect( render_anchors )
|
||||
rect_margins := range2_to_rl_rect( render_margins )
|
||||
rect_bounds := range2_to_rl_rect( render_bounds )
|
||||
rect_padding := range2_to_rl_rect( render_padding )
|
||||
rect_content := range2_to_rl_rect( render_content )
|
||||
// profile_end()
|
||||
|
||||
// profile_begin("rl.DrawRectangleRounded( rect_bounds, style.layout.corner_radii[0], 9, style.bg_color )")
|
||||
if style.bg_color.a != 0
|
||||
{
|
||||
draw_rectangle( rect_bounds, style )
|
||||
}
|
||||
if style.border_width > 0 {
|
||||
draw_rectangle_lines( rect_bounds, style, style.border_color, style.border_width )
|
||||
}
|
||||
// profile_end()
|
||||
|
||||
line_thickness : f32 = 1
|
||||
|
||||
// profile_begin("rl.DrawRectangleRoundedLines: padding & content")
|
||||
if equal_range2(computed.content, computed.padding) {
|
||||
// draw_rectangle_lines( rect_padding, style, Color_Debug_UI_Padding_Bounds, line_thickness )
|
||||
}
|
||||
else {
|
||||
// draw_rectangle_lines( rect_content, style, Color_Debug_UI_Content_Bounds, line_thickness )
|
||||
}
|
||||
// profile_end()
|
||||
|
||||
if .Mouse_Resizable in current.flags
|
||||
{
|
||||
// profile("Resize Bounds")
|
||||
resize_border_width := cast(f32) get_state().config.ui_resize_border_width
|
||||
resize_percent_width := computed_size * (resize_border_width * 1.0/ 200.0)
|
||||
resize_border_non_range := add(current.computed.bounds, range2(
|
||||
{ resize_percent_width.x, -resize_percent_width.x },
|
||||
{ -resize_percent_width.x, resize_percent_width.x }))
|
||||
|
||||
render_resize := range2(
|
||||
resize_border_non_range.min,
|
||||
resize_border_non_range.max,
|
||||
)
|
||||
rect_resize := rl.Rectangle {
|
||||
render_resize.min.x,
|
||||
render_resize.min.y,
|
||||
render_resize.max.x - render_resize.min.x,
|
||||
render_resize.max.y - render_resize.min.y,
|
||||
}
|
||||
draw_rectangle_lines( rect_padding, style, Color_Red, line_thickness )
|
||||
}
|
||||
|
||||
point_radius : f32 = 3
|
||||
|
||||
// profile_begin("circles")
|
||||
// center := Vec2 {
|
||||
// render_bounds.p0.x + computed_size.x * 0.5,
|
||||
// render_bounds.p0.y - computed_size.y * 0.5,
|
||||
// }
|
||||
// rl.DrawCircleV( center, point_radius, Color_White )
|
||||
|
||||
// rl.DrawCircleV( render_bounds.p0, point_radius, Color_Red )
|
||||
// rl.DrawCircleV( render_bounds.p1, point_radius, Color_Blue )
|
||||
// profile_end()
|
||||
|
||||
if len(current.text.str) > 0 {
|
||||
draw_text_screenspace( current.text, surface_to_render_pos(computed.text_pos), style.font_size, style.text_color )
|
||||
}
|
||||
}
|
||||
}
|
||||
//endregion App UI
|
||||
}
|
||||
|
@ -149,6 +149,7 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
}
|
||||
|
||||
//region 2D Camera Manual Nav
|
||||
// TODO(Ed): This should be per workspace view
|
||||
{
|
||||
// profile("Camera Manual Nav")
|
||||
digital_move_speed : f32 = 200.0
|
||||
@ -184,13 +185,314 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
if debug_actions.cam_mouse_pan
|
||||
{
|
||||
if is_within_screenspace(input.mouse.pos) {
|
||||
pan_velocity := input.mouse.delta * ( 1 / cam.zoom )
|
||||
pan_velocity := input.mouse.delta * vec2(1, -1) * ( 1 / cam.zoom )
|
||||
cam.target -= pan_velocity
|
||||
}
|
||||
}
|
||||
}
|
||||
//endregion 2D Camera Manual Nav
|
||||
|
||||
// TODO(Ed): We need input buffer so that we can consume input actions based on the UI with priority
|
||||
|
||||
//region App UI Tick
|
||||
{
|
||||
profile("App Screenspace Imgui")
|
||||
|
||||
ui_graph_build( & state.app_ui )
|
||||
ui := ui_context
|
||||
|
||||
/*
|
||||
Prototype app menu
|
||||
This is a menu bar for the app for now inside the same ui as the workspace's UI state
|
||||
Eventually this will get moved out to its own UI state for the app itself.
|
||||
*/
|
||||
if true
|
||||
{
|
||||
fmt :: str_fmt_alloc
|
||||
|
||||
@static bar_pos := Vec2{}
|
||||
bar_size := vec2( 400, 40 )
|
||||
|
||||
menu_bar : UI_Widget
|
||||
{
|
||||
theme := UI_Style {
|
||||
flags = {
|
||||
},
|
||||
bg_color = { 0, 0, 0, 30 },
|
||||
border_color = { 0, 0, 0, 200 },
|
||||
|
||||
font = default_font,
|
||||
font_size = 12,
|
||||
text_color = Color_White,
|
||||
|
||||
layout = UI_Layout {
|
||||
anchor = {},
|
||||
border_width = 1.0,
|
||||
pos = bar_pos,
|
||||
size = range2( bar_size, {}),
|
||||
// padding = { 10, 10, 10, 10 },
|
||||
},
|
||||
}
|
||||
ui_theme_via_style(theme)
|
||||
menu_bar = ui_widget("App Menu Bar", UI_BoxFlags {} )
|
||||
menu_bar.text = { fmt("%v", bar_pos), {} }
|
||||
menu_bar.text.runes = to_runes(menu_bar.text.str)
|
||||
|
||||
if (menu_bar.first_frame) {
|
||||
bar_pos = screen_get_corners().top_right - vec2(0, app_window.extent.y * 0.5)
|
||||
}
|
||||
}
|
||||
// Setup Children
|
||||
settings_btn : UI_Widget
|
||||
{
|
||||
ui_parent(menu_bar)
|
||||
|
||||
style := UI_Style {
|
||||
flags = {
|
||||
// .Origin_At_Anchor_Center
|
||||
.Fixed_Height
|
||||
},
|
||||
bg_color = Color_Frame_Disabled,
|
||||
|
||||
font = default_font,
|
||||
font_size = 18,
|
||||
text_color = Color_White,
|
||||
|
||||
layout = UI_Layout {
|
||||
anchor = range2( {0, 0}, {0.0, 0} ),
|
||||
alignment = { 0.0, 1.0 },
|
||||
text_alignment = { 0.5, 0.5 },
|
||||
pos = { 0, 0 },
|
||||
size = range2( {25, bar_size.y}, {0, 0})
|
||||
}
|
||||
}
|
||||
theme := UI_StyleTheme { styles = {
|
||||
style,
|
||||
style,
|
||||
style,
|
||||
style,
|
||||
}}
|
||||
theme.disabled.bg_color = Color_Frame_Disabled
|
||||
theme.hot.bg_color = Color_White
|
||||
theme.active.bg_color = Color_Frame_Select
|
||||
ui_style_theme(theme)
|
||||
|
||||
move_box : UI_Widget
|
||||
{
|
||||
move_box = ui_button("Move Box")
|
||||
if move_box.dragging {
|
||||
bar_pos += input.mouse.delta
|
||||
}
|
||||
}
|
||||
// bar_pos = {0, 400}
|
||||
|
||||
move_settings_spacer := ui_widget("Move-Settings Spacer", {})
|
||||
move_settings_spacer.text = str_intern("spacer")
|
||||
move_settings_spacer.style.font_size = 10
|
||||
move_settings_spacer.style.bg_color = Color_Transparent
|
||||
|
||||
// settings_btn : UI_Widget
|
||||
{
|
||||
settings_btn = ui_button("Settings Btn")
|
||||
settings_btn.text = str_intern("Settings")
|
||||
settings_btn.style.flags = {
|
||||
.Scale_Width_By_Height_Ratio,
|
||||
}
|
||||
}
|
||||
|
||||
// HBox layout calculation?
|
||||
{
|
||||
hb_space_ratio_move_box := 0.1
|
||||
hb_space_ratio_move_settings_spacer := 0.05
|
||||
hb_space_ratio_settings_btn := 1.0
|
||||
|
||||
style := & move_box.box.style
|
||||
style.anchor.max.x = 0.9
|
||||
|
||||
style = & move_settings_spacer.box.style
|
||||
style.anchor.min.x = 0.1
|
||||
style.anchor.max.x = 0.8
|
||||
|
||||
style = & settings_btn.box.style
|
||||
style.anchor.min.x = 0.2
|
||||
style.anchor.max.x = 0.55
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@static settings_open := true
|
||||
if settings_btn.left_clicked || settings_open
|
||||
{
|
||||
settings_open = true
|
||||
|
||||
@static pos := Vec2 {0, 0}
|
||||
@static size := Vec2 { 600, 800 }
|
||||
resize_border_width : f32 = 10
|
||||
|
||||
// Prototype for a resize box
|
||||
// Will surround one box with a resize borders
|
||||
// All sides can have their borders toggled
|
||||
resize_box := ui_widget("Settings Menu: Resize Box", {})
|
||||
{
|
||||
using resize_box
|
||||
style.pos = pos
|
||||
style.alignment = { 0.5, 0.5 }
|
||||
style.bg_color = {}
|
||||
style.size = range2( size, {})
|
||||
}
|
||||
ui_parent(resize_box)
|
||||
|
||||
// Resize handles and corners
|
||||
{
|
||||
flags := UI_BoxFlags { .Mouse_Clickable, .Focusable }
|
||||
|
||||
style_resize_width := UI_Style {
|
||||
flags = { .Fixed_Width },
|
||||
size = range2({resize_border_width, 0}, {}),
|
||||
bg_color = Color_ResizeHandle,
|
||||
alignment = {0, 1},
|
||||
margins = { resize_border_width, resize_border_width, 0, 0 },
|
||||
}
|
||||
style_resize_height := style_resize_width
|
||||
style_resize_height.flags = {.Fixed_Height}
|
||||
style_resize_height.size.min = {0, resize_border_width}
|
||||
style_resize_height.margins = { 0, 0, resize_border_width, resize_border_width }
|
||||
|
||||
ui_theme_via_style(style_resize_width)
|
||||
left := ui_widget("Settings Menu: Resize Left Border", flags )
|
||||
right := ui_widget("Settings Menu: Resize Right Border", flags)
|
||||
right.style.anchor.left = 1
|
||||
right.style.alignment = {1, 1}
|
||||
|
||||
ui_theme_via_style(style_resize_height)
|
||||
top := ui_widget("Settings Menu: Resize Top Border", flags )
|
||||
bottom := ui_widget("Settings Menu: Resize Bottom Border", flags)
|
||||
bottom.style.anchor.top = 1
|
||||
bottom.style.alignment = {0, 0}
|
||||
|
||||
style_corner := UI_Style {
|
||||
flags = { .Fixed_Width, .Fixed_Height },
|
||||
size = range2({resize_border_width, resize_border_width}, {}),
|
||||
bg_color = Color_Blue,
|
||||
alignment = {0, 1}
|
||||
}
|
||||
ui_theme_via_style(style_corner)
|
||||
corner_tl := ui_widget("Settings Menu: Corner TL", flags)
|
||||
corner_tr := ui_widget("Settings Menu: Corner TR", flags)
|
||||
corner_tr.style.anchor = range2({1, 0}, {})
|
||||
corner_tr.style.alignment = {1, 1}
|
||||
|
||||
corner_bl := ui_widget("Settings Menu: Corner BL", flags)
|
||||
corner_bl.style.anchor = range2({}, {0, 1})
|
||||
corner_bl.style.alignment = {}
|
||||
corner_br := ui_widget("Settings Menu: Corner BR", flags)
|
||||
corner_br.style.anchor = range2({1, 0}, {0, 1})
|
||||
corner_br.style.alignment = {1, 0}
|
||||
|
||||
process_handle_drag :: #force_inline proc ( handle : ^UI_Widget,
|
||||
side_scalar : f32,
|
||||
size_axis : ^f32,
|
||||
size_delta_axis : f32,
|
||||
pos_axis : ^f32,
|
||||
alignment_axis : ^f32,
|
||||
alignment_while_dragging : f32 )
|
||||
{
|
||||
if get_state().ui_context.last_pressed_key != handle.key { return }
|
||||
|
||||
@static was_dragging := false
|
||||
@static within_drag := false
|
||||
|
||||
using handle.signal
|
||||
if dragging
|
||||
{
|
||||
if ! was_dragging {
|
||||
was_dragging = true
|
||||
(pos_axis ^) += size_axis^ * 0.5 * side_scalar
|
||||
}
|
||||
(size_axis ^) += size_delta_axis * -side_scalar
|
||||
(alignment_axis ^) = alignment_while_dragging
|
||||
}
|
||||
else if was_dragging && released
|
||||
{
|
||||
(pos_axis ^) += size_axis^ * 0.5 * -side_scalar
|
||||
was_dragging = false
|
||||
}
|
||||
}
|
||||
|
||||
process_handle_drag( & right , -1.0, & size.x, input.mouse.delta.x, & pos.x, & resize_box.style.alignment.x, 0)
|
||||
process_handle_drag( & left, 1.0, & size.x, input.mouse.delta.x, & pos.x, & resize_box.style.alignment.x, 1)
|
||||
process_handle_drag( & top, -1.0, & size.y, input.mouse.delta.y, & pos.y, & resize_box.style.alignment.y, 0)
|
||||
process_handle_drag( & bottom, 1.0, & size.y, input.mouse.delta.y, & pos.y, & resize_box.style.alignment.y, 1)
|
||||
|
||||
// process_corner_drag :: #force_inline proc()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
settings_menu := ui_widget("Settings Menu", {})
|
||||
{
|
||||
using settings_menu
|
||||
style.alignment = { 0.0, 1.0 }
|
||||
style.bg_color = Color_BG_Panel_Translucent
|
||||
// style.border_width = 1.0
|
||||
// style.border_color = Color_Blue
|
||||
style.margins = {
|
||||
resize_border_width,
|
||||
resize_border_width,
|
||||
resize_border_width,
|
||||
resize_border_width, }
|
||||
}
|
||||
ui_parent(settings_menu)
|
||||
|
||||
ui_theme_via_style({
|
||||
bg_color = Color_Transparent,
|
||||
font = default_font,
|
||||
font_size = 16,
|
||||
text_color = Color_White,
|
||||
size = range2({0, 40}, {0, 40}) // TODO(Ed): Implment ratio scaling for height
|
||||
})
|
||||
frame_bar := ui_widget("Settings Menu: Frame Bar", { .Mouse_Clickable, .Focusable, .Click_To_Focus })
|
||||
{
|
||||
using frame_bar
|
||||
style.bg_color = Color_BG_Panel
|
||||
style.flags = {}
|
||||
style.alignment = { 0, 1 }
|
||||
// style.size = {}
|
||||
style.anchor = range2( {0, 0.95}, {0, 0} )
|
||||
ui_parent(frame_bar)
|
||||
|
||||
if dragging {
|
||||
pos += input.mouse.delta
|
||||
}
|
||||
|
||||
title := ui_text("Settings Menu: Title", str_intern("Settings Menu"))
|
||||
{
|
||||
using title
|
||||
style.alignment = {0, 1}
|
||||
style.margins = { 0, 0, 15, 0}
|
||||
style.text_alignment = {0.0 , 0.5}
|
||||
}
|
||||
|
||||
close_btn := ui_button("Settings Menu: Close Btn")
|
||||
{
|
||||
using close_btn
|
||||
text = str_intern("close")
|
||||
style.bg_color = Color_GreyRed
|
||||
style.size.min = {50, 0}
|
||||
style.anchor = range2( {1.0, 0}, {})
|
||||
style.alignment = {1, 1}
|
||||
style.text_alignment = {0.5, 0.5}
|
||||
if close_btn.pressed {
|
||||
settings_open = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//endregion App UI Tick
|
||||
|
||||
//region WorkspaceImgui Tick
|
||||
{
|
||||
profile("Workspace Imgui")
|
||||
@ -240,180 +542,9 @@ update :: proc( delta_time : f64 ) -> b32
|
||||
// test_text_box()
|
||||
// test_parenting( & default_layout, & frame_style_default )
|
||||
// test_whitespace_ast( & default_layout, & frame_style_default )
|
||||
|
||||
/*
|
||||
Prototype app menu
|
||||
This is a menu bar for the app for now inside the same ui as the workspace's UI state
|
||||
Eventually this will get moved out to its own UI state for the app itself.
|
||||
*/
|
||||
if true
|
||||
{
|
||||
fmt :: str_fmt_alloc
|
||||
|
||||
@static bar_pos := Vec2 {}
|
||||
bar_size := vec2( 400, 40 )
|
||||
|
||||
menu_bar : UI_Widget
|
||||
{
|
||||
theme := UI_Style {
|
||||
flags = {
|
||||
},
|
||||
bg_color = { 0, 0, 0, 30 },
|
||||
border_color = { 0, 0, 0, 200 },
|
||||
|
||||
font = default_font,
|
||||
font_size = 12,
|
||||
text_color = Color_White,
|
||||
|
||||
layout = UI_Layout {
|
||||
anchor = {},
|
||||
border_width = 1.0 * (1.0/cam.zoom),
|
||||
pos = screen_to_world(bar_pos),
|
||||
size = range2( bar_size * (1.0/cam.zoom), {}),
|
||||
// padding = { 10, 10, 10, 10 },
|
||||
},
|
||||
}
|
||||
ui_theme_via_style(theme)
|
||||
menu_bar = ui_widget("App Menu Bar", UI_BoxFlags {} )
|
||||
}
|
||||
// Setup Children
|
||||
settings_btn : UI_Widget
|
||||
{
|
||||
ui_parent(menu_bar)
|
||||
|
||||
style := UI_Style {
|
||||
flags = {
|
||||
// .Origin_At_Anchor_Center
|
||||
.Fixed_Height
|
||||
},
|
||||
bg_color = Color_Frame_Disabled,
|
||||
|
||||
font = default_font,
|
||||
font_size = 18 * (1.0/cam.zoom),
|
||||
text_color = Color_White,
|
||||
|
||||
layout = UI_Layout {
|
||||
anchor = range2( {0, 0}, {0.0, 0} ),
|
||||
alignment = { 0.0, 1.0 },
|
||||
text_alignment = { 0.5, 0.5 },
|
||||
pos = { 0, 0 },
|
||||
size = range2( {25, bar_size.y} * (1.0/cam.zoom), {0, 0})
|
||||
}
|
||||
}
|
||||
theme := UI_StyleTheme { styles = {
|
||||
style,
|
||||
style,
|
||||
style,
|
||||
style,
|
||||
}}
|
||||
theme.disabled.bg_color = Color_Frame_Disabled
|
||||
theme.hot.bg_color = Color_White
|
||||
theme.active.bg_color = Color_Frame_Select
|
||||
ui_style_theme(theme)
|
||||
|
||||
move_box : UI_Widget
|
||||
{
|
||||
move_box = ui_button("Move Box")
|
||||
if move_box.dragging {
|
||||
// bar_pos += mouse_world_delta()
|
||||
bar_pos += state.input.mouse.delta
|
||||
}
|
||||
}
|
||||
|
||||
move_settings_spacer := ui_widget("Move-Settings Spacer", {})
|
||||
move_settings_spacer.text = str_intern("")
|
||||
move_settings_spacer.style.font_size = 10 * (1.0/cam.zoom)
|
||||
move_settings_spacer.style.bg_color = Color_Transparent
|
||||
|
||||
// settings_btn : UI_Widget
|
||||
{
|
||||
settings_btn = ui_button("Settings Btn")
|
||||
settings_btn.text = str_intern("Settings")
|
||||
settings_btn.style.flags = {
|
||||
.Scale_Width_By_Height_Ratio,
|
||||
}
|
||||
}
|
||||
|
||||
// HBox layout calculation?
|
||||
{
|
||||
hb_space_ratio_move_box := 0.1
|
||||
hb_space_ratio_move_settings_spacer := 0.05
|
||||
hb_space_ratio_settings_btn := 1.0
|
||||
|
||||
style := & move_box.box.style
|
||||
style.anchor.max.x = 0.9
|
||||
|
||||
style = & move_settings_spacer.box.style
|
||||
style.anchor.min.x = 0.1
|
||||
style.anchor.max.x = 0.8
|
||||
|
||||
style = & settings_btn.box.style
|
||||
style.anchor.min.x = 0.2
|
||||
style.anchor.max.x = 0.55
|
||||
}
|
||||
}
|
||||
|
||||
@static settings_open := false
|
||||
if settings_btn.left_clicked || settings_open
|
||||
{
|
||||
settings_open = true
|
||||
|
||||
@static pos := Vec2 {0, 0}
|
||||
|
||||
settings_menu := ui_widget("Settings Menu", { .Mouse_Clickable, .Focusable, .Click_To_Focus })
|
||||
settings_menu.style.pos = screen_to_world(pos)
|
||||
settings_menu.style.size = range2( {600, 800} * (1/cam.zoom), {})
|
||||
settings_menu.style.text_alignment = {0, 0.0}
|
||||
settings_menu.style.alignment = { 0.5, 0.5 }
|
||||
settings_menu.style.bg_color = Color_Transparent
|
||||
settings_menu.style.border_width = 1.0 * (1/cam.zoom)
|
||||
settings_menu.style.border_color = Color_Blue
|
||||
// settings_menu.style.padding = { 10, 10, 10, 10 }
|
||||
|
||||
settings_menu.text = { fmt("%v", pos), {} }
|
||||
settings_menu.text.runes = to_runes(settings_menu.text.str)
|
||||
settings_menu.style.font_size = 16 * (1/cam.zoom)
|
||||
|
||||
// pos.x += frametime_delta32() * 100
|
||||
if settings_menu.dragging {
|
||||
pos += state.input.mouse.delta
|
||||
// pos.x += frametime_delta32() * 1
|
||||
}
|
||||
ui_parent(settings_menu)
|
||||
|
||||
frame_bar := ui_widget("Settings Menu: Frame Bar", {})
|
||||
{
|
||||
using frame_bar
|
||||
// style.bg_color = Color_Red
|
||||
style.flags = {}
|
||||
style.alignment = { 0, 1 }
|
||||
style.size = {}
|
||||
style.anchor = range2( {0, 0.95}, {0, 0} )
|
||||
|
||||
// Close button
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
//endregion Workspace Imgui Tick
|
||||
|
||||
//region App Screenspace Imgui Tick
|
||||
{
|
||||
profile("App Screenspace Imgui")
|
||||
|
||||
ui_graph_build( & state.app_ui )
|
||||
ui := ui_context
|
||||
|
||||
/*
|
||||
Prototype app menu
|
||||
TODO(Ed): Move it to here
|
||||
*/
|
||||
}
|
||||
//endregion App Screenspace Imgui Tick
|
||||
|
||||
debug.last_mouse_pos = input.mouse.pos
|
||||
|
||||
should_shutdown : b32 = ! cast(b32) rl.WindowShouldClose()
|
||||
|
187
code/ui.odin
187
code/ui.odin
@ -69,6 +69,8 @@ UI_BoxFlag :: enum u64 {
|
||||
Pan_X,
|
||||
Pan_Y,
|
||||
|
||||
Screenspace,
|
||||
|
||||
Count,
|
||||
}
|
||||
UI_BoxFlags :: bit_set[UI_BoxFlag; u64]
|
||||
@ -86,13 +88,6 @@ UI_Computed :: struct {
|
||||
text_size : Vec2, // Size of text within content
|
||||
}
|
||||
|
||||
UI_LayoutSide :: struct {
|
||||
// using _ : struct {
|
||||
top, bottom : UI_Scalar,
|
||||
left, right : UI_Scalar,
|
||||
// }
|
||||
}
|
||||
|
||||
UI_Cursor :: struct {
|
||||
placeholder : int,
|
||||
}
|
||||
@ -119,32 +114,6 @@ UI_ScalarConstraint :: struct {
|
||||
|
||||
UI_Scalar2 :: [Axis2.Count]UI_Scalar
|
||||
|
||||
// Desiered constraints on the UI_Box.
|
||||
UI_Layout :: struct {
|
||||
anchor : Range2,
|
||||
alignment : Vec2,
|
||||
text_alignment : Vec2,
|
||||
|
||||
border_width : UI_Scalar,
|
||||
|
||||
margins : UI_LayoutSide,
|
||||
padding : UI_LayoutSide,
|
||||
|
||||
// TODO(Ed): We cannot support individual corners unless we add it to raylib (or finally change the rendering backend)
|
||||
corner_radii : [Corner.Count]f32,
|
||||
|
||||
// Position in relative coordinate space.
|
||||
// If the box's flags has Fixed_Position, then this will be its aboslute position in the relative coordinate space
|
||||
pos : Vec2,
|
||||
|
||||
size : Range2,
|
||||
|
||||
// TODO(Ed) : Should thsi just always be WS_Pos for workspace UI?
|
||||
// (We can union either varient and just know based on checking if its the screenspace UI)
|
||||
// If the box is a child of the root parent, its automatically in world space and thus will use the tile_pos.
|
||||
// tile_pos : WS_Pos,
|
||||
}
|
||||
|
||||
UI_Signal :: struct {
|
||||
cursor_pos : Vec2,
|
||||
drag_delta : Vec2,
|
||||
@ -163,91 +132,6 @@ UI_Signal :: struct {
|
||||
commit : b8,
|
||||
}
|
||||
|
||||
UI_StyleFlag :: enum u32 {
|
||||
|
||||
// Will perform scissor pass on children to their parent's bounds
|
||||
// (Specified in the parent)
|
||||
Clip_Children_To_Bounds,
|
||||
|
||||
// Enforces the box will always remain in a specific position relative to the parent.
|
||||
// Overriding the anchors and margins.
|
||||
Fixed_Position_X,
|
||||
Fixed_Position_Y,
|
||||
|
||||
// Enforces box will always be within the bounds of the parent box.
|
||||
Clamp_Position_X,
|
||||
Clamp_Position_Y,
|
||||
|
||||
// Enroces the widget will maintain its size reguardless of any constraints
|
||||
// Will override parent constraints (use the size.min.xy to specify the width & height)
|
||||
Fixed_Width,
|
||||
Fixed_Height,
|
||||
|
||||
// TODO(Ed): Implement this!
|
||||
// Enforces the widget will have a width specified as a ratio of its height (use the size.min/max.x to specify the scalar)
|
||||
// If you wish for the width to stay fixed couple with the Fixed_Width flag
|
||||
Scale_Width_By_Height_Ratio,
|
||||
// Enforces the widget will have a height specified as a ratio of its width (use the size.min/max.y to specify the scalar)
|
||||
// If you wish for the height to stay fixed couple with the Fixed_Height flag
|
||||
Scale_Height_By_Width_Ratio,
|
||||
|
||||
// Sets the (0, 0) position of the child box to the parents anchor's center (post-margins bounds)
|
||||
// By Default, the origin is at the top left of the anchor's bounds
|
||||
Origin_At_Anchor_Center,
|
||||
|
||||
// Will size the box to its text. (Padding & Margins will thicken )
|
||||
Size_To_Text,
|
||||
Text_Wrap,
|
||||
|
||||
Count,
|
||||
}
|
||||
UI_StyleFlags :: bit_set[UI_StyleFlag; u32]
|
||||
|
||||
UI_StylePreset :: enum u32 {
|
||||
Default,
|
||||
Disabled,
|
||||
Hot,
|
||||
Active,
|
||||
Count,
|
||||
}
|
||||
|
||||
UI_Style :: struct {
|
||||
flags : UI_StyleFlags,
|
||||
|
||||
bg_color : Color,
|
||||
border_color : Color,
|
||||
|
||||
// TODO(Ed) : Add support for this eventually
|
||||
blur_size : f32,
|
||||
|
||||
font : FontID,
|
||||
// TODO(Ed): Should this get moved to the layout struct? Techncially font-size is mainly
|
||||
font_size : f32,
|
||||
text_color : Color,
|
||||
|
||||
cursor : UI_Cursor,
|
||||
|
||||
using layout : UI_Layout,
|
||||
|
||||
// Used with style, prev_style, and style_delta to produce a simple interpolated animation
|
||||
// Applied in the layout pass & the rendering pass for their associated fields.
|
||||
transition_time : f32,
|
||||
}
|
||||
|
||||
UI_StyleTheme :: struct #raw_union {
|
||||
array : [UI_StylePreset.Count] UI_Style,
|
||||
using styles : struct {
|
||||
default, disabled, hot, active : UI_Style,
|
||||
}
|
||||
}
|
||||
|
||||
UI_TextAlign :: enum u32 {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
Count
|
||||
}
|
||||
|
||||
UI_Box :: struct {
|
||||
// Cache ID
|
||||
key : UI_Key,
|
||||
@ -443,16 +327,16 @@ ui_box_tranverse_next :: proc "contextless" ( box : ^ UI_Box ) -> (^ UI_Box)
|
||||
ui_cursor_pos :: #force_inline proc "contextless" () -> Vec2 {
|
||||
using state := get_state()
|
||||
if ui_context == & state.project.workspace.ui {
|
||||
return screen_to_world( input.mouse.pos )
|
||||
return surface_to_ws_view_pos( input.mouse.pos )
|
||||
}
|
||||
else {
|
||||
return input.mouse.pos
|
||||
}
|
||||
}
|
||||
|
||||
ui_drag_delta :: #force_inline proc "contextless" () -> Vec2 {
|
||||
ui_ws_drag_delta :: #force_inline proc "contextless" () -> Vec2 {
|
||||
using state := get_state()
|
||||
return ui_cursor_pos() - state.ui_context.active_start_signal.cursor_pos
|
||||
return surface_to_ws_view_pos(input.mouse.pos) - state.ui_context.active_start_signal.cursor_pos
|
||||
}
|
||||
|
||||
ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} )
|
||||
@ -481,19 +365,19 @@ ui_graph_build_begin :: proc( ui : ^ UI_State, bounds : Vec2 = {} )
|
||||
}
|
||||
|
||||
// TODO(Ed) :: Is this even needed?
|
||||
ui_graph_build_end :: proc()
|
||||
ui_graph_build_end :: proc( ui : ^UI_State )
|
||||
{
|
||||
profile(#procedure)
|
||||
|
||||
ui_parent_pop() // Should be ui_context.root
|
||||
|
||||
// Regenerate the computed layout if dirty
|
||||
ui_compute_layout()
|
||||
ui_compute_layout( ui )
|
||||
|
||||
get_state().ui_context = nil
|
||||
}
|
||||
|
||||
@(deferred_none = ui_graph_build_end)
|
||||
@(deferred_in = ui_graph_build_end)
|
||||
ui_graph_build :: proc( ui : ^ UI_State ) {
|
||||
ui_graph_build_begin( ui )
|
||||
}
|
||||
@ -520,10 +404,6 @@ ui_key_from_string :: #force_inline proc "contextless" ( value : string ) -> UI_
|
||||
return key
|
||||
}
|
||||
|
||||
ui_layout_padding :: proc( pixels : f32 ) -> UI_LayoutSide {
|
||||
return { pixels, pixels, pixels, pixels }
|
||||
}
|
||||
|
||||
ui_parent_push :: proc( ui : ^ UI_Box ) {
|
||||
stack := & get_state().ui_context.parent_stack
|
||||
stack_push( & get_state().ui_context.parent_stack, ui )
|
||||
@ -542,54 +422,3 @@ ui_parent_pop :: proc() {
|
||||
ui_parent :: proc( ui : ^UI_Box) {
|
||||
ui_parent_push( ui )
|
||||
}
|
||||
|
||||
ui_style_peek :: proc( box_state : UI_StylePreset ) -> UI_Style {
|
||||
return stack_peek_ref( & get_state().ui_context.theme_stack ).array[box_state]
|
||||
}
|
||||
|
||||
ui_style_ref :: proc( box_state : UI_StylePreset ) -> (^ UI_Style) {
|
||||
return & stack_peek_ref( & get_state().ui_context.theme_stack ).array[box_state]
|
||||
}
|
||||
|
||||
ui_style_set :: proc ( style : UI_Style, box_state : UI_StylePreset ) {
|
||||
stack_peek_ref( & get_state().ui_context.theme_stack ).array[box_state] = style
|
||||
}
|
||||
|
||||
ui_style_set_layout :: proc ( layout : UI_Layout, preset : UI_StylePreset ) {
|
||||
stack_peek_ref( & get_state().ui_context.theme_stack ).array[preset].layout = layout
|
||||
}
|
||||
|
||||
ui_style_theme_push :: proc( preset : UI_StyleTheme ) {
|
||||
push( & get_state().ui_context.theme_stack, preset )
|
||||
}
|
||||
|
||||
ui_style_theme_pop :: proc() {
|
||||
pop( & get_state().ui_context.theme_stack )
|
||||
}
|
||||
|
||||
@(deferred_none = ui_style_theme_pop)
|
||||
ui_style_theme :: proc( preset : UI_StyleTheme ) {
|
||||
ui_style_theme_push( preset )
|
||||
}
|
||||
|
||||
@(deferred_none = ui_style_theme_pop)
|
||||
ui_theme_via_style :: proc ( style : UI_Style ) {
|
||||
ui_style_theme_push( UI_StyleTheme { styles = { style, style, style, style } })
|
||||
}
|
||||
|
||||
ui_style_theme_set_layout :: proc ( layout : UI_Layout ) {
|
||||
for & preset in stack_peek_ref( & get_state().ui_context.theme_stack ).array {
|
||||
preset.layout = layout
|
||||
}
|
||||
}
|
||||
|
||||
ui_style_theme_layout_push :: proc ( layout : UI_Layout ) {
|
||||
ui := get_state().ui_context
|
||||
ui_style_theme_push( stack_peek( & ui.theme_stack) )
|
||||
ui_style_theme_set_layout(layout)
|
||||
}
|
||||
|
||||
@(deferred_none = ui_style_theme_pop)
|
||||
ui_style_theme_layout :: proc( layout : UI_Layout ) {
|
||||
ui_style_theme_layout_push(layout)
|
||||
}
|
||||
|
@ -5,12 +5,12 @@ import "core:math/linalg"
|
||||
|
||||
// Note(Ed): This is naturally pretty expensive
|
||||
|
||||
ui_compute_layout :: proc()
|
||||
ui_compute_layout :: proc( ui : ^UI_State )
|
||||
{
|
||||
profile(#procedure)
|
||||
state := get_state()
|
||||
|
||||
root := state.project.workspace.ui.root
|
||||
root := ui.root
|
||||
{
|
||||
computed := & root.computed
|
||||
style := root.style
|
||||
@ -172,7 +172,7 @@ ui_compute_layout :: proc()
|
||||
text_pos.y += ( content_size.y - text_size.y ) * layout.text_alignment.y
|
||||
|
||||
computed.text_size = text_size
|
||||
computed.text_pos = { text_pos.x, -text_pos.y }
|
||||
computed.text_pos = { text_pos.x, text_pos.y }
|
||||
}
|
||||
|
||||
current = ui_box_tranverse_next( current )
|
||||
|
174
code/ui_style.odin
Normal file
174
code/ui_style.odin
Normal file
@ -0,0 +1,174 @@
|
||||
package sectr
|
||||
|
||||
UI_LayoutSide :: struct {
|
||||
// using _ : struct {
|
||||
top, bottom : UI_Scalar,
|
||||
left, right : UI_Scalar,
|
||||
// }
|
||||
}
|
||||
|
||||
// Desiered constraints on the UI_Box.
|
||||
UI_Layout :: struct {
|
||||
anchor : Range2,
|
||||
alignment : Vec2,
|
||||
text_alignment : Vec2,
|
||||
|
||||
border_width : UI_Scalar,
|
||||
|
||||
margins : UI_LayoutSide,
|
||||
padding : UI_LayoutSide,
|
||||
|
||||
// TODO(Ed): We cannot support individual corners unless we add it to raylib (or finally change the rendering backend)
|
||||
corner_radii : [Corner.Count]f32,
|
||||
|
||||
// Position in relative coordinate space.
|
||||
// If the box's flags has Fixed_Position, then this will be its aboslute position in the relative coordinate space
|
||||
pos : Vec2,
|
||||
|
||||
size : Range2,
|
||||
|
||||
// TODO(Ed) : Should thsi just always be WS_Pos for workspace UI?
|
||||
// (We can union either varient and just know based on checking if its the screenspace UI)
|
||||
// If the box is a child of the root parent, its automatically in world space and thus will use the tile_pos.
|
||||
// tile_pos : WS_Pos,
|
||||
}
|
||||
|
||||
UI_StyleFlag :: enum u32 {
|
||||
|
||||
// Will perform scissor pass on children to their parent's bounds
|
||||
// (Specified in the parent)
|
||||
Clip_Children_To_Bounds,
|
||||
|
||||
// Enforces the box will always remain in a specific position relative to the parent.
|
||||
// Overriding the anchors and margins.
|
||||
Fixed_Position_X,
|
||||
Fixed_Position_Y,
|
||||
|
||||
// Enforces box will always be within the bounds of the parent box.
|
||||
Clamp_Position_X,
|
||||
Clamp_Position_Y,
|
||||
|
||||
// Enroces the widget will maintain its size reguardless of any constraints
|
||||
// Will override parent constraints (use the size.min.xy to specify the width & height)
|
||||
Fixed_Width,
|
||||
Fixed_Height,
|
||||
|
||||
// TODO(Ed): Implement this!
|
||||
// Enforces the widget will have a width specified as a ratio of its height (use the size.min/max.x to specify the scalar)
|
||||
// If you wish for the width to stay fixed couple with the Fixed_Width flag
|
||||
Scale_Width_By_Height_Ratio,
|
||||
// Enforces the widget will have a height specified as a ratio of its width (use the size.min/max.y to specify the scalar)
|
||||
// If you wish for the height to stay fixed couple with the Fixed_Height flag
|
||||
Scale_Height_By_Width_Ratio,
|
||||
|
||||
// Sets the (0, 0) position of the child box to the parents anchor's center (post-margins bounds)
|
||||
// By Default, the origin is at the top left of the anchor's bounds
|
||||
Origin_At_Anchor_Center,
|
||||
|
||||
// Will size the box to its text. (Padding & Margins will thicken )
|
||||
Size_To_Text,
|
||||
Text_Wrap,
|
||||
|
||||
Count,
|
||||
}
|
||||
UI_StyleFlags :: bit_set[UI_StyleFlag; u32]
|
||||
|
||||
UI_StylePreset :: enum u32 {
|
||||
Default,
|
||||
Disabled,
|
||||
Hot,
|
||||
Active,
|
||||
Count,
|
||||
}
|
||||
|
||||
UI_Style :: struct {
|
||||
flags : UI_StyleFlags,
|
||||
|
||||
bg_color : Color,
|
||||
border_color : Color,
|
||||
|
||||
// TODO(Ed) : Add support for this eventually
|
||||
blur_size : f32,
|
||||
|
||||
font : FontID,
|
||||
// TODO(Ed): Should this get moved to the layout struct? Techncially font-size is mainly
|
||||
font_size : f32,
|
||||
text_color : Color,
|
||||
|
||||
cursor : UI_Cursor,
|
||||
|
||||
using layout : UI_Layout,
|
||||
|
||||
// Used with style, prev_style, and style_delta to produce a simple interpolated animation
|
||||
// Applied in the layout pass & the rendering pass for their associated fields.
|
||||
transition_time : f32,
|
||||
}
|
||||
|
||||
UI_StyleTheme :: struct #raw_union {
|
||||
array : [UI_StylePreset.Count] UI_Style,
|
||||
using styles : struct {
|
||||
default, disabled, hot, active : UI_Style,
|
||||
}
|
||||
}
|
||||
|
||||
UI_TextAlign :: enum u32 {
|
||||
Left,
|
||||
Center,
|
||||
Right,
|
||||
Count
|
||||
}
|
||||
|
||||
ui_layout_padding :: proc( pixels : f32 ) -> UI_LayoutSide {
|
||||
return { pixels, pixels, pixels, pixels }
|
||||
}
|
||||
|
||||
ui_style_peek :: proc( box_state : UI_StylePreset ) -> UI_Style {
|
||||
return stack_peek_ref( & get_state().ui_context.theme_stack ).array[box_state]
|
||||
}
|
||||
|
||||
ui_style_ref :: proc( box_state : UI_StylePreset ) -> (^ UI_Style) {
|
||||
return & stack_peek_ref( & get_state().ui_context.theme_stack ).array[box_state]
|
||||
}
|
||||
|
||||
ui_style_set :: proc ( style : UI_Style, box_state : UI_StylePreset ) {
|
||||
stack_peek_ref( & get_state().ui_context.theme_stack ).array[box_state] = style
|
||||
}
|
||||
|
||||
ui_style_set_layout :: proc ( layout : UI_Layout, preset : UI_StylePreset ) {
|
||||
stack_peek_ref( & get_state().ui_context.theme_stack ).array[preset].layout = layout
|
||||
}
|
||||
|
||||
ui_style_theme_push :: proc( preset : UI_StyleTheme ) {
|
||||
push( & get_state().ui_context.theme_stack, preset )
|
||||
}
|
||||
|
||||
ui_style_theme_pop :: proc() {
|
||||
pop( & get_state().ui_context.theme_stack )
|
||||
}
|
||||
|
||||
@(deferred_none = ui_style_theme_pop)
|
||||
ui_style_theme :: proc( preset : UI_StyleTheme ) {
|
||||
ui_style_theme_push( preset )
|
||||
}
|
||||
|
||||
@(deferred_none = ui_style_theme_pop)
|
||||
ui_theme_via_style :: proc ( style : UI_Style ) {
|
||||
ui_style_theme_push( UI_StyleTheme { styles = { style, style, style, style } })
|
||||
}
|
||||
|
||||
ui_style_theme_set_layout :: proc ( layout : UI_Layout ) {
|
||||
for & preset in stack_peek_ref( & get_state().ui_context.theme_stack ).array {
|
||||
preset.layout = layout
|
||||
}
|
||||
}
|
||||
|
||||
ui_style_theme_layout_push :: proc ( layout : UI_Layout ) {
|
||||
ui := get_state().ui_context
|
||||
ui_style_theme_push( stack_peek( & ui.theme_stack) )
|
||||
ui_style_theme_set_layout(layout)
|
||||
}
|
||||
|
||||
@(deferred_none = ui_style_theme_pop)
|
||||
ui_style_theme_layout :: proc( layout : UI_Layout ) {
|
||||
ui_style_theme_layout_push(layout)
|
||||
}
|
@ -66,6 +66,9 @@ test_draggable :: proc()
|
||||
|
||||
draggable.style.layout.pos = debug.draggable_box_pos
|
||||
draggable.style.layout.size.min = debug.draggable_box_size
|
||||
|
||||
draggable.text = { str_fmt_alloc("%v", debug.draggable_box_pos), {} }
|
||||
draggable.text.runes = to_runes(draggable.text.str)
|
||||
}
|
||||
|
||||
test_parenting :: proc( default_layout : ^UI_Layout, frame_style_default : ^UI_Style )
|
||||
|
12
ols.json
12
ols.json
@ -1,18 +1,6 @@
|
||||
{
|
||||
"$schema": "https://raw.githubusercontent.com/DanielGavin/ols/master/misc/ols.schema.json",
|
||||
"collections": [
|
||||
{
|
||||
"name": "core",
|
||||
"path": "C:/projects/SectrPrototype/toolchain/Odin/core"
|
||||
},
|
||||
{
|
||||
"name": "vendor",
|
||||
"path": "C:/projects/SectrPrototype/toolchain/Odin/vendor"
|
||||
},
|
||||
{
|
||||
"name": "code",
|
||||
"path": "C:/projects/SectrPrototype/code"
|
||||
},
|
||||
{
|
||||
"name": "thirdparty",
|
||||
"path": "C:/projects/SectrPrototype/thirdparty"
|
||||
|
Loading…
Reference in New Issue
Block a user