working-ish state for the app & some gui work done on the packing screen
This commit is contained in:
parent
8da1321bdd
commit
8d36f8bbd8
6 changed files with 349 additions and 51 deletions
10
.vscode/settings.json
vendored
10
.vscode/settings.json
vendored
|
|
@ -1,7 +1,7 @@
|
||||||
{
|
{
|
||||||
"workbench.colorCustomizations": {
|
"workbench.colorCustomizations": {
|
||||||
"activityBar.background": "#322C2D",
|
"activityBar.background": "#322C2D",
|
||||||
"titleBar.activeBackground": "#463E3F",
|
"titleBar.activeBackground": "#463E3F",
|
||||||
"titleBar.activeForeground": "#FAFAFA"
|
"titleBar.activeForeground": "#FAFAFA"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
34
src/dialog/tinyfiledialog.odin
Normal file
34
src/dialog/tinyfiledialog.odin
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
package tinyfiledialogs
|
||||||
|
|
||||||
|
import "core:c"
|
||||||
|
|
||||||
|
when ODIN_OS == .Windows {
|
||||||
|
foreign import lib {"tinyfiledialogs.lib", "system:comdlg32.lib", "system:Ole32.lib"}
|
||||||
|
}
|
||||||
|
when ODIN_OS == .Linux {
|
||||||
|
|
||||||
|
foreign import lib {"libtinyfiledialogs.a"}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreign lib {
|
||||||
|
@(link_name = "tinyfd_notifyPopup")
|
||||||
|
notify_popup :: proc(title, message, icon_type: cstring) -> c.int ---
|
||||||
|
|
||||||
|
@(link_name = "tinyfd_messageBox")
|
||||||
|
message_box :: proc(title, message, dialog_type, icon_type: cstring, default_button: c.int) -> c.int ---
|
||||||
|
|
||||||
|
@(link_name = "tinyfd_inputBox")
|
||||||
|
input_box :: proc(title, message, default_input: cstring) -> [^]c.char ---
|
||||||
|
|
||||||
|
@(link_name = "tinyfd_saveFileDialog")
|
||||||
|
save_file_dialog :: proc(title, default_path: cstring, pattern_count: c.int, patterns: [^]cstring, file_desc: cstring) -> [^]c.char ---
|
||||||
|
|
||||||
|
@(link_name = "tinyfd_openFileDialog")
|
||||||
|
open_file_dialog :: proc(title, default_path: cstring, pattern_count: c.int, patterns: [^]cstring, file_desc: cstring, allow_multi: c.int) -> [^]c.char ---
|
||||||
|
|
||||||
|
@(link_name = "tinyfd_selectFolderDialog")
|
||||||
|
select_folder_dialog :: proc(title, default_path: cstring) -> [^]c.char ---
|
||||||
|
|
||||||
|
@(link_name = "tinyfd_colorChooser")
|
||||||
|
color_chooser :: proc(title, default_hex_rgb: cstring, default_rgb, result_rgb: [3]byte) -> [^]c.char ---
|
||||||
|
}
|
||||||
318
src/game.odin
318
src/game.odin
|
|
@ -11,15 +11,68 @@
|
||||||
|
|
||||||
package game
|
package game
|
||||||
|
|
||||||
// import "core:fmt"
|
import "core:fmt"
|
||||||
// import "core:math/linalg"
|
import "core:strings"
|
||||||
import rl "vendor:raylib"
|
import rl "vendor:raylib"
|
||||||
|
|
||||||
|
import diag "dialog"
|
||||||
|
|
||||||
PixelWindowHeight :: 180
|
PixelWindowHeight :: 180
|
||||||
|
|
||||||
|
/*
|
||||||
|
`SourceFilesPicker` // Screen 1: Shows the file dialog box, meant for the user to choose the source files/folder
|
||||||
|
`OutputLocationPicker` // Screen 2: Shows the file dialog box, meant for the user to choose the output file name & location
|
||||||
|
`PackSettingsAndPreview` Screen 3: Shows settings about the packing operations, `save` & `save as` button
|
||||||
|
`SaveToOutputPicker` // Screen 4: After clicking the `save as` button on screen 3, ask the user for a new location & name and save the file
|
||||||
|
*/
|
||||||
|
|
||||||
|
AppScreen :: enum {
|
||||||
|
SourceFilesPicker,
|
||||||
|
OutputLocationPicker,
|
||||||
|
PackSettingsAndPreview,
|
||||||
|
SaveToOutputPicker,
|
||||||
|
}
|
||||||
|
|
||||||
|
WindowInformation :: struct {
|
||||||
|
w: f32,
|
||||||
|
h: f32,
|
||||||
|
width_scaled: f32,
|
||||||
|
height_scaled: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
MonitorInformation :: struct {
|
||||||
|
max_width: f32,
|
||||||
|
max_height: f32,
|
||||||
|
}
|
||||||
|
|
||||||
|
FileDialogType :: enum {
|
||||||
|
SourceFiles,
|
||||||
|
SourceFolder,
|
||||||
|
OutputFolder,
|
||||||
|
Exit,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
FILE_DIALOG_SIZE :: 1000
|
FILE_DIALOG_SIZE :: 1000
|
||||||
GameMemory :: struct {
|
GameMemory :: struct {
|
||||||
file_dialog_text_buffer: [FILE_DIALOG_SIZE + 1]u8,
|
file_dialog_text_buffer: [FILE_DIALOG_SIZE + 1]u8,
|
||||||
|
is_packing_whole_source_folder: bool,
|
||||||
|
should_open_file_dialog: bool,
|
||||||
|
window_info: WindowInformation,
|
||||||
|
monitor_info: MonitorInformation,
|
||||||
|
// atlas packer state
|
||||||
|
app_screen: AppScreen,
|
||||||
|
// Where the output files will be written (atlas.png, json output, etc)
|
||||||
|
output_path_set: bool,
|
||||||
|
output_folder_path: string,
|
||||||
|
// If files were chosen as input - their paths
|
||||||
|
input_path_set: bool,
|
||||||
|
source_location_to_pack: string,
|
||||||
|
// If a folder was chosen as input - the path
|
||||||
|
input_files_set: bool,
|
||||||
|
source_files_to_pack: []string,
|
||||||
|
// What type of file dialog to open
|
||||||
|
source_location_type: FileDialogType,
|
||||||
}
|
}
|
||||||
|
|
||||||
g_mem: ^GameMemory
|
g_mem: ^GameMemory
|
||||||
|
|
@ -35,7 +88,6 @@ game_camera :: proc() -> rl.Camera2D {
|
||||||
|
|
||||||
scaling: f32 = 2
|
scaling: f32 = 2
|
||||||
ui_camera :: proc() -> rl.Camera2D {
|
ui_camera :: proc() -> rl.Camera2D {
|
||||||
// return {zoom = f32(rl.GetScreenHeight()) / PixelWindowHeight}
|
|
||||||
return {zoom = scaling}
|
return {zoom = scaling}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -43,35 +95,249 @@ input_box_loc: rl.Vector2 = {}
|
||||||
moving_input_box: bool
|
moving_input_box: bool
|
||||||
update :: proc() {
|
update :: proc() {
|
||||||
// Update the width/height
|
// Update the width/height
|
||||||
w = f32(rl.GetScreenWidth())
|
win_info := &g_mem.window_info
|
||||||
h = f32(rl.GetScreenHeight())
|
win_info.w = f32(rl.GetScreenWidth())
|
||||||
|
win_info.h = f32(rl.GetScreenHeight())
|
||||||
|
win_info.height_scaled = win_info.h / scaling
|
||||||
|
win_info.width_scaled = win_info.w / scaling
|
||||||
|
w = win_info.w
|
||||||
|
h = win_info.h
|
||||||
|
|
||||||
|
// Update the virtual mouse position (needed for GUI interaction to work properly for instance)
|
||||||
rl.SetMouseScale(1 / scaling, 1 / scaling)
|
rl.SetMouseScale(1 / scaling, 1 / scaling)
|
||||||
|
|
||||||
if rl.IsMouseButtonDown(.RIGHT) {
|
update_screen()
|
||||||
input_box_loc = rl.GetMousePosition()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
draw :: proc() {
|
draw :: proc() {
|
||||||
rl.BeginDrawing()
|
rl.BeginDrawing()
|
||||||
defer rl.EndDrawing()
|
defer rl.EndDrawing()
|
||||||
|
|
||||||
rl.ClearBackground(rl.BLACK)
|
rl.ClearBackground(rl.BLACK)
|
||||||
|
|
||||||
rl.BeginMode2D(ui_camera())
|
draw_screen_ui()
|
||||||
rl.GuiTextInputBox(
|
|
||||||
rl.Rectangle {
|
draw_screen_target()
|
||||||
x = input_box_loc.x,
|
}
|
||||||
y = input_box_loc.y,
|
|
||||||
width = (w / scaling) / 2,
|
update_screen :: proc() {
|
||||||
height = (h / scaling) / 2,
|
if (g_mem.input_files_set || g_mem.input_path_set) {
|
||||||
},
|
if !g_mem.output_path_set {
|
||||||
"Files",
|
g_mem.app_screen = .OutputLocationPicker
|
||||||
"File input box",
|
} else {
|
||||||
"Button",
|
g_mem.app_screen = .PackSettingsAndPreview
|
||||||
cstring(rawptr(&g_mem.file_dialog_text_buffer)),
|
}
|
||||||
FILE_DIALOG_SIZE,
|
} else {
|
||||||
nil,
|
g_mem.app_screen = .SourceFilesPicker
|
||||||
)
|
}
|
||||||
rl.EndMode2D()
|
|
||||||
|
switch g_mem.app_screen {
|
||||||
|
case .SourceFilesPicker:
|
||||||
|
fallthrough
|
||||||
|
case .OutputLocationPicker:
|
||||||
|
fallthrough
|
||||||
|
case .SaveToOutputPicker:
|
||||||
|
if g_mem.should_open_file_dialog {
|
||||||
|
open_file_dialog_and_store_output_paths()
|
||||||
|
}
|
||||||
|
case .PackSettingsAndPreview:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_screen_ui :: proc() {
|
||||||
|
rl.BeginMode2D(ui_camera())
|
||||||
|
defer rl.EndMode2D()
|
||||||
|
|
||||||
|
switch g_mem.app_screen {
|
||||||
|
case .SourceFilesPicker:
|
||||||
|
fallthrough
|
||||||
|
case .OutputLocationPicker:
|
||||||
|
fallthrough
|
||||||
|
case .SaveToOutputPicker:
|
||||||
|
draw_and_handle_source_files_logic()
|
||||||
|
case .PackSettingsAndPreview:
|
||||||
|
draw_atlas_settings_and_preview()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_screen_target :: proc() {
|
||||||
|
rl.BeginMode2D(ui_camera())
|
||||||
|
defer rl.EndMode2D()
|
||||||
|
|
||||||
|
}
|
||||||
|
draw_atlas_settings_and_preview :: proc() {
|
||||||
|
left_half_rect := rl.Rectangle {
|
||||||
|
0,
|
||||||
|
0,
|
||||||
|
auto_cast g_mem.window_info.width_scaled / 2,
|
||||||
|
auto_cast g_mem.window_info.height_scaled,
|
||||||
|
}
|
||||||
|
right_half_rect := rl.Rectangle {
|
||||||
|
auto_cast g_mem.window_info.width_scaled / 2,
|
||||||
|
0,
|
||||||
|
auto_cast g_mem.window_info.width_scaled / 2,
|
||||||
|
auto_cast g_mem.window_info.height_scaled,
|
||||||
|
}
|
||||||
|
rl.DrawRectangleRec(left_half_rect, rl.WHITE)
|
||||||
|
rl.DrawRectangleRec(right_half_rect, rl.MAROON)
|
||||||
|
|
||||||
|
// font := rl.GuiGetFont()
|
||||||
|
// text_size := rl.MeasureTextEx(font, "Atlas Packer Settings", auto_cast font.baseSize / scaling, 1)
|
||||||
|
|
||||||
|
small_offset := 10 * scaling
|
||||||
|
elements_height: f32 = 0
|
||||||
|
rl.GuiLabel(
|
||||||
|
rl.Rectangle{x = small_offset, y = 0, width = 100 * scaling, height = 25 * scaling},
|
||||||
|
"Atlas Packer Settings",
|
||||||
|
)
|
||||||
|
elements_height += 25 * scaling
|
||||||
|
rl.GuiLine({y = elements_height, width = left_half_rect.width}, "Packer Settings")
|
||||||
|
elements_height += small_offset
|
||||||
|
rl.GuiLine({y = elements_height, width = left_half_rect.width}, "Save Settings")
|
||||||
|
elements_height += small_offset
|
||||||
|
if rl.GuiButton(
|
||||||
|
{
|
||||||
|
x = small_offset,
|
||||||
|
y = elements_height,
|
||||||
|
width = left_half_rect.width / 2 - small_offset * 2,
|
||||||
|
height = 25 * scaling,
|
||||||
|
},
|
||||||
|
"Save",
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
if rl.GuiButton(
|
||||||
|
{
|
||||||
|
x = left_half_rect.width / 2,
|
||||||
|
y = elements_height,
|
||||||
|
width = left_half_rect.width / 2 - small_offset,
|
||||||
|
height = 25 * scaling,
|
||||||
|
},
|
||||||
|
"Save To...",
|
||||||
|
) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
open_file_dialog_and_store_output_paths :: proc() {
|
||||||
|
if g_mem.source_location_type == .SourceFiles {
|
||||||
|
files := cstring(
|
||||||
|
diag.open_file_dialog(
|
||||||
|
"Select source files",
|
||||||
|
cstring(&g_mem.file_dialog_text_buffer[0]),
|
||||||
|
0,
|
||||||
|
nil,
|
||||||
|
"",
|
||||||
|
1,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
source_files_to_pack := strings.clone_from_cstring(files, context.allocator)
|
||||||
|
// File dialog returns an array of path(s), separated by a '|'
|
||||||
|
g_mem.source_files_to_pack = strings.split(source_files_to_pack, "|")
|
||||||
|
g_mem.input_files_set = (len(source_files_to_pack) > 0)
|
||||||
|
|
||||||
|
fmt.println(g_mem.source_files_to_pack)
|
||||||
|
}
|
||||||
|
if g_mem.source_location_type == .SourceFolder {
|
||||||
|
file := cstring(
|
||||||
|
diag.select_folder_dialog(
|
||||||
|
"Select source folder",
|
||||||
|
cstring(&g_mem.file_dialog_text_buffer[0]),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
g_mem.source_location_to_pack = strings.clone_from_cstring(file)
|
||||||
|
g_mem.input_path_set = (len(file) > 0)
|
||||||
|
fmt.println(g_mem.source_location_to_pack)
|
||||||
|
}
|
||||||
|
if g_mem.source_location_type == .OutputFolder {
|
||||||
|
file := cstring(
|
||||||
|
diag.select_folder_dialog(
|
||||||
|
"Select source folder",
|
||||||
|
cstring(&g_mem.file_dialog_text_buffer[0]),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
g_mem.output_folder_path = strings.clone_from_cstring(file)
|
||||||
|
|
||||||
|
g_mem.output_path_set = (len(file) > 0)
|
||||||
|
fmt.println(g_mem.output_folder_path)
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mem.should_open_file_dialog = false
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_and_handle_source_files_logic :: proc() {
|
||||||
|
switch g_mem.app_screen {
|
||||||
|
case .SourceFilesPicker:
|
||||||
|
result := rl.GuiTextInputBox(
|
||||||
|
rl.Rectangle{width = (w / scaling), height = (h / scaling)},
|
||||||
|
"Files",
|
||||||
|
"File input box",
|
||||||
|
"Open Source Files;Open Source Folder",
|
||||||
|
cstring(rawptr(&g_mem.file_dialog_text_buffer)),
|
||||||
|
FILE_DIALOG_SIZE,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if result != -1 {
|
||||||
|
file_dialg_type: FileDialogType
|
||||||
|
if result == 1 || result == 2 {
|
||||||
|
file_dialg_type = .SourceFiles if result == 1 else .SourceFolder
|
||||||
|
} else if result == 0 {
|
||||||
|
file_dialg_type = .Exit
|
||||||
|
}
|
||||||
|
handle_source_file_logic(file_dialg_type)
|
||||||
|
fmt.println("result: ", result)
|
||||||
|
}
|
||||||
|
case .OutputLocationPicker:
|
||||||
|
result := rl.GuiTextInputBox(
|
||||||
|
rl.Rectangle{width = (w / scaling), height = (h / scaling)},
|
||||||
|
"Files",
|
||||||
|
"Output Folder",
|
||||||
|
"Choose Output Folder",
|
||||||
|
cstring(rawptr(&g_mem.file_dialog_text_buffer)),
|
||||||
|
FILE_DIALOG_SIZE,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if result != -1 {
|
||||||
|
file_dialg_type: FileDialogType = .OutputFolder if result == 1 else .Exit
|
||||||
|
handle_source_file_logic(file_dialg_type)
|
||||||
|
fmt.println("result: ", result)
|
||||||
|
}
|
||||||
|
case .SaveToOutputPicker:
|
||||||
|
result := rl.GuiTextInputBox(
|
||||||
|
rl.Rectangle{width = (w / scaling), height = (h / scaling)},
|
||||||
|
"Files",
|
||||||
|
"Output Folder",
|
||||||
|
"Choose Output Folder",
|
||||||
|
cstring(rawptr(&g_mem.file_dialog_text_buffer)),
|
||||||
|
FILE_DIALOG_SIZE,
|
||||||
|
nil,
|
||||||
|
)
|
||||||
|
if result != -1 {
|
||||||
|
file_dialg_type: FileDialogType = .SourceFolder if result == 1 else .Exit
|
||||||
|
handle_source_file_logic(file_dialg_type)
|
||||||
|
fmt.println("result: ", result)
|
||||||
|
}
|
||||||
|
case .PackSettingsAndPreview:
|
||||||
|
draw_packer_and_settings()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_packer_and_settings :: proc() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
handle_source_file_logic :: proc(picker_type: FileDialogType) {
|
||||||
|
switch picker_type {
|
||||||
|
case .Exit:
|
||||||
|
g_mem.should_open_file_dialog = false
|
||||||
|
rl.CloseWindow()
|
||||||
|
case .SourceFiles:
|
||||||
|
fallthrough
|
||||||
|
case .SourceFolder:
|
||||||
|
fallthrough
|
||||||
|
case .OutputFolder:
|
||||||
|
g_mem.source_location_type = picker_type
|
||||||
|
g_mem.should_open_file_dialog = true
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
package game
|
|
||||||
|
|
||||||
import rl "vendor:raylib"
|
|
||||||
|
|
||||||
// Check if any key is pressed
|
|
||||||
// NOTE: We limit keys check to keys between 32 (KEY_SPACE) and 126
|
|
||||||
IsAnyKeyPressed :: proc() -> (keyPressed: bool) {
|
|
||||||
key := rl.GetKeyPressed()
|
|
||||||
|
|
||||||
if (i32(key) >= 32) && (i32(key) <= 126) {
|
|
||||||
keyPressed = true
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
@ -12,19 +12,31 @@ game_update :: proc() -> bool {
|
||||||
@(export)
|
@(export)
|
||||||
game_init_window :: proc() {
|
game_init_window :: proc() {
|
||||||
rl.SetConfigFlags({.WINDOW_RESIZABLE})
|
rl.SetConfigFlags({.WINDOW_RESIZABLE})
|
||||||
rl.InitWindow(1280, 720, "Odin + Raylib + Hot Reload template!")
|
rl.InitWindow(1280, 720, "YAAP - Yet Another Atlas Packer, Powered by Raylib & Odin")
|
||||||
rl.SetWindowPosition(200, 200)
|
rl.SetWindowPosition(200, 200)
|
||||||
rl.SetTargetFPS(500)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@(export)
|
@(export)
|
||||||
game_init :: proc() {
|
game_init :: proc() {
|
||||||
g_mem = new(GameMemory)
|
g_mem = new(GameMemory)
|
||||||
|
|
||||||
g_mem^ = GameMemory {
|
g_mem^ = GameMemory{}
|
||||||
}
|
|
||||||
|
|
||||||
game_hot_reloaded(g_mem)
|
game_hot_reloaded(g_mem)
|
||||||
|
|
||||||
|
current_monitor := rl.GetCurrentMonitor()
|
||||||
|
g_mem.monitor_info = MonitorInformation {
|
||||||
|
max_width = auto_cast rl.GetMonitorWidth(current_monitor),
|
||||||
|
max_height = auto_cast rl.GetMonitorHeight(current_monitor),
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mem.window_info = WindowInformation {
|
||||||
|
w = 1280,
|
||||||
|
h = 720,
|
||||||
|
}
|
||||||
|
|
||||||
|
rl.SetTargetFPS(rl.GetMonitorRefreshRate(current_monitor))
|
||||||
|
rl.GuiLoadStyle("./styles/style_candy.rgs")
|
||||||
}
|
}
|
||||||
|
|
||||||
@(export)
|
@(export)
|
||||||
|
|
@ -50,6 +62,7 @@ game_memory_size :: proc() -> int {
|
||||||
@(export)
|
@(export)
|
||||||
game_hot_reloaded :: proc(mem: rawptr) {
|
game_hot_reloaded :: proc(mem: rawptr) {
|
||||||
g_mem = (^GameMemory)(mem)
|
g_mem = (^GameMemory)(mem)
|
||||||
|
rl.GuiLoadStyle("./styles/style_candy.rgs")
|
||||||
}
|
}
|
||||||
|
|
||||||
@(export)
|
@(export)
|
||||||
|
|
@ -60,4 +73,4 @@ game_force_reload :: proc() -> bool {
|
||||||
@(export)
|
@(export)
|
||||||
game_force_restart :: proc() -> bool {
|
game_force_restart :: proc() -> bool {
|
||||||
return rl.IsKeyPressed(.F6)
|
return rl.IsKeyPressed(.F6)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
BIN
styles/style_candy.rgs
Normal file
BIN
styles/style_candy.rgs
Normal file
Binary file not shown.
Loading…
Add table
Add a link
Reference in a new issue