np
This commit is contained in:
parent
1e85df5193
commit
33c70e776a
@ -9,12 +9,12 @@
|
|||||||
"last_modified_iso": ""
|
"last_modified_iso": ""
|
||||||
},
|
},
|
||||||
"user_mode": 0,
|
"user_mode": 0,
|
||||||
"story_idea": "two balls rolling under the sun",
|
"story_idea": "3 trees on the wind",
|
||||||
"story_genre": "action",
|
"story_genre": "action",
|
||||||
"target_audience": "general",
|
"target_audience": "general",
|
||||||
"art_style": "manga",
|
"art_style": "manga",
|
||||||
"script": {
|
"script": {
|
||||||
"title": "Rolling Duel",
|
"title": "The Last Stand",
|
||||||
"synopsis": "Generated comic synopsis",
|
"synopsis": "Generated comic synopsis",
|
||||||
"characters": [
|
"characters": [
|
||||||
|
|
||||||
@ -28,7 +28,7 @@
|
|||||||
"panel_id": "panel_001_001",
|
"panel_id": "panel_001_001",
|
||||||
"panel_number": 1,
|
"panel_number": 1,
|
||||||
"shot_type": 2,
|
"shot_type": 2,
|
||||||
"description": "A blazing sun dominates the sky, casting harsh light on a vast, empty desert. Two small dots in the distance kick up dust.",
|
"description": "Wide shot: Three ancient trees stand on a barren hilltop, their branches intertwined. Storm clouds swirl overhead, lightning in the distance. Wind howls, leaves flying.",
|
||||||
"characters_present": [
|
"characters_present": [
|
||||||
|
|
||||||
],
|
],
|
||||||
@ -45,7 +45,7 @@
|
|||||||
"panel_id": "panel_001_002",
|
"panel_id": "panel_001_002",
|
||||||
"panel_number": 2,
|
"panel_number": 2,
|
||||||
"shot_type": 2,
|
"shot_type": 2,
|
||||||
"description": "Close-up on two balls: one red with a fiery pattern, one blue with a water-like swirl. They are rolling fast, side by side. Cracks form in the ground beneath them.",
|
"description": "Close-up on the middle tree's trunk. Bark cracks open, revealing a glowing, pulsing core of light. The other two trees lean inward, as if protecting it.",
|
||||||
"characters_present": [
|
"characters_present": [
|
||||||
|
|
||||||
],
|
],
|
||||||
@ -62,17 +62,12 @@
|
|||||||
"panel_id": "panel_001_003",
|
"panel_id": "panel_001_003",
|
||||||
"panel_number": 3,
|
"panel_number": 3,
|
||||||
"shot_type": 2,
|
"shot_type": 2,
|
||||||
"description": "The red ball veers sharply left, kicking up a spray of sand. The blue ball mirrors the move, sparks flying from its surface.",
|
"description": "From the left, a massive tornado approaches, dark and funnel-shaped. Debris swirls around it. The trees brace, roots gripping the ground.",
|
||||||
"characters_present": [
|
"characters_present": [
|
||||||
|
|
||||||
],
|
],
|
||||||
"dialogue": [
|
"dialogue": [
|
||||||
{
|
|
||||||
"speaker_id": "",
|
|
||||||
"text": "VROOM!",
|
|
||||||
"bubble_type": 0,
|
|
||||||
"emotion": ""
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"caption": "",
|
"caption": "",
|
||||||
"sound_effects": [
|
"sound_effects": [
|
||||||
@ -84,29 +79,7 @@
|
|||||||
"panel_id": "panel_001_004",
|
"panel_id": "panel_001_004",
|
||||||
"panel_number": 4,
|
"panel_number": 4,
|
||||||
"shot_type": 2,
|
"shot_type": 2,
|
||||||
"description": "Red ball takes a ramp-like dune and launches into the air, spinning. Blue ball follows, but slightly lower.",
|
"description": "The tornado hits the left tree. Its branches snap violently, but it holds firm, roots glowing with energy. Sparks fly where wind meets bark.",
|
||||||
"characters_present": [
|
|
||||||
|
|
||||||
],
|
|
||||||
"dialogue": [
|
|
||||||
{
|
|
||||||
"speaker_id": "",
|
|
||||||
"text": "WHOOSH!",
|
|
||||||
"bubble_type": 0,
|
|
||||||
"emotion": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"caption": "",
|
|
||||||
"sound_effects": [
|
|
||||||
|
|
||||||
],
|
|
||||||
"transition_from_previous": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"panel_id": "panel_001_005",
|
|
||||||
"panel_number": 5,
|
|
||||||
"shot_type": 2,
|
|
||||||
"description": "Aerial view: both balls are airborne, shadows on the sand below. Red ball is slightly ahead.",
|
|
||||||
"characters_present": [
|
"characters_present": [
|
||||||
|
|
||||||
],
|
],
|
||||||
@ -116,28 +89,6 @@
|
|||||||
"caption": "",
|
"caption": "",
|
||||||
"sound_effects": [
|
"sound_effects": [
|
||||||
|
|
||||||
],
|
|
||||||
"transition_from_previous": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"panel_id": "panel_001_006",
|
|
||||||
"panel_number": 6,
|
|
||||||
"shot_type": 2,
|
|
||||||
"description": "They land simultaneously, creating twin craters. Dust clouds obscure them. The sun glints off their surfaces.",
|
|
||||||
"characters_present": [
|
|
||||||
|
|
||||||
],
|
|
||||||
"dialogue": [
|
|
||||||
{
|
|
||||||
"speaker_id": "",
|
|
||||||
"text": "BOOM!",
|
|
||||||
"bubble_type": 0,
|
|
||||||
"emotion": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"caption": "",
|
|
||||||
"sound_effects": [
|
|
||||||
|
|
||||||
],
|
],
|
||||||
"transition_from_previous": 0
|
"transition_from_previous": 0
|
||||||
}
|
}
|
||||||
@ -151,7 +102,7 @@
|
|||||||
"panel_id": "panel_002_001",
|
"panel_id": "panel_002_001",
|
||||||
"panel_number": 1,
|
"panel_number": 1,
|
||||||
"shot_type": 2,
|
"shot_type": 2,
|
||||||
"description": "From the dust, the red ball emerges first, rolling faster. The blue ball is close behind, leaving a trail of steam.",
|
"description": "The middle tree pulses brighter, sending a shockwave that pushes the tornado back. The right tree extends a branch to shield the core. Wind howls.",
|
||||||
"characters_present": [
|
"characters_present": [
|
||||||
|
|
||||||
],
|
],
|
||||||
@ -168,17 +119,12 @@
|
|||||||
"panel_id": "panel_002_002",
|
"panel_id": "panel_002_002",
|
||||||
"panel_number": 2,
|
"panel_number": 2,
|
||||||
"shot_type": 2,
|
"shot_type": 2,
|
||||||
"description": "Close-up on the red ball: its surface is glowing hot, with tiny flames licking the edges.",
|
"description": "The tornado splits into two smaller funnels, attacking from both sides. The left tree's roots snap, it starts to topple. The middle tree's core flickers.",
|
||||||
"characters_present": [
|
"characters_present": [
|
||||||
|
|
||||||
],
|
],
|
||||||
"dialogue": [
|
"dialogue": [
|
||||||
{
|
|
||||||
"speaker_id": "",
|
|
||||||
"text": "HISS",
|
|
||||||
"bubble_type": 0,
|
|
||||||
"emotion": ""
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"caption": "",
|
"caption": "",
|
||||||
"sound_effects": [
|
"sound_effects": [
|
||||||
@ -190,17 +136,12 @@
|
|||||||
"panel_id": "panel_002_003",
|
"panel_id": "panel_002_003",
|
||||||
"panel_number": 3,
|
"panel_number": 3,
|
||||||
"shot_type": 2,
|
"shot_type": 2,
|
||||||
"description": "The blue ball rams into the red ball from the side. They lock, spinning together in a whirlwind of sand.",
|
"description": "The right tree bends forward, its trunk wrapping around the middle tree, absorbing the impact. The left tree falls, but its roots still glow, transferring energy.",
|
||||||
"characters_present": [
|
"characters_present": [
|
||||||
|
|
||||||
],
|
],
|
||||||
"dialogue": [
|
"dialogue": [
|
||||||
{
|
|
||||||
"speaker_id": "",
|
|
||||||
"text": "CLANG!",
|
|
||||||
"bubble_type": 0,
|
|
||||||
"emotion": ""
|
|
||||||
}
|
|
||||||
],
|
],
|
||||||
"caption": "",
|
"caption": "",
|
||||||
"sound_effects": [
|
"sound_effects": [
|
||||||
@ -212,29 +153,7 @@
|
|||||||
"panel_id": "panel_002_004",
|
"panel_id": "panel_002_004",
|
||||||
"panel_number": 4,
|
"panel_number": 4,
|
||||||
"shot_type": 2,
|
"shot_type": 2,
|
||||||
"description": "They separate, skidding to a halt. Both balls are facing each other, a few meters apart. The sun is directly overhead.",
|
"description": "Final wide shot: The storm passes, clouds break. The two remaining trees stand tall, the middle core glowing steady. A single leaf drifts down, landing on the ground. Peace.",
|
||||||
"characters_present": [
|
|
||||||
|
|
||||||
],
|
|
||||||
"dialogue": [
|
|
||||||
{
|
|
||||||
"speaker_id": "",
|
|
||||||
"text": "SCREECH",
|
|
||||||
"bubble_type": 0,
|
|
||||||
"emotion": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"caption": "",
|
|
||||||
"sound_effects": [
|
|
||||||
|
|
||||||
],
|
|
||||||
"transition_from_previous": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"panel_id": "panel_002_005",
|
|
||||||
"panel_number": 5,
|
|
||||||
"shot_type": 2,
|
|
||||||
"description": "Silence. A single bead of sweat (or condensation) drips from the blue ball. The red ball's glow intensifies.",
|
|
||||||
"characters_present": [
|
"characters_present": [
|
||||||
|
|
||||||
],
|
],
|
||||||
@ -244,28 +163,6 @@
|
|||||||
"caption": "",
|
"caption": "",
|
||||||
"sound_effects": [
|
"sound_effects": [
|
||||||
|
|
||||||
],
|
|
||||||
"transition_from_previous": 0
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"panel_id": "panel_002_006",
|
|
||||||
"panel_number": 6,
|
|
||||||
"shot_type": 2,
|
|
||||||
"description": "Both balls lunge forward at the same time. The panel is a blur of motion lines and dust. The final word:",
|
|
||||||
"characters_present": [
|
|
||||||
|
|
||||||
],
|
|
||||||
"dialogue": [
|
|
||||||
{
|
|
||||||
"speaker_id": "",
|
|
||||||
"text": "CRASH!!!",
|
|
||||||
"bubble_type": 0,
|
|
||||||
"emotion": ""
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"caption": "",
|
|
||||||
"sound_effects": [
|
|
||||||
|
|
||||||
],
|
],
|
||||||
"transition_from_previous": 0
|
"transition_from_previous": 0
|
||||||
}
|
}
|
||||||
@ -277,90 +174,7 @@
|
|||||||
|
|
||||||
],
|
],
|
||||||
"panel_images": {
|
"panel_images": {
|
||||||
"panel_001_001": {
|
|
||||||
"url": "file:///tmp/comic-gui-local-panels-1597088181/panel_001_panel_001_001.png",
|
|
||||||
"width": 1024,
|
|
||||||
"height": 1024,
|
|
||||||
"seed": 1,
|
|
||||||
"prompt": "local"
|
|
||||||
},
|
|
||||||
"panel_002_006": {
|
|
||||||
"url": "file:///tmp/comic-gui-local-panels-1597088181/panel_012_panel_002_006.png",
|
|
||||||
"width": 1024,
|
|
||||||
"height": 1024,
|
|
||||||
"seed": 12,
|
|
||||||
"prompt": "local"
|
|
||||||
},
|
|
||||||
"panel_001_006": {
|
|
||||||
"url": "file:///tmp/comic-gui-local-panels-1597088181/panel_006_panel_001_006.png",
|
|
||||||
"width": 1024,
|
|
||||||
"height": 1024,
|
|
||||||
"seed": 6,
|
|
||||||
"prompt": "local"
|
|
||||||
},
|
|
||||||
"panel_002_001": {
|
|
||||||
"url": "file:///tmp/comic-gui-local-panels-1597088181/panel_007_panel_002_001.png",
|
|
||||||
"width": 1024,
|
|
||||||
"height": 1024,
|
|
||||||
"seed": 7,
|
|
||||||
"prompt": "local"
|
|
||||||
},
|
|
||||||
"panel_001_002": {
|
|
||||||
"url": "file:///tmp/comic-gui-local-panels-1597088181/panel_002_panel_001_002.png",
|
|
||||||
"width": 1024,
|
|
||||||
"height": 1024,
|
|
||||||
"seed": 2,
|
|
||||||
"prompt": "local"
|
|
||||||
},
|
|
||||||
"panel_002_005": {
|
|
||||||
"url": "file:///tmp/comic-gui-local-panels-1597088181/panel_011_panel_002_005.png",
|
|
||||||
"width": 1024,
|
|
||||||
"height": 1024,
|
|
||||||
"seed": 11,
|
|
||||||
"prompt": "local"
|
|
||||||
},
|
|
||||||
"panel_001_004": {
|
|
||||||
"url": "file:///tmp/comic-gui-local-panels-1597088181/panel_004_panel_001_004.png",
|
|
||||||
"width": 1024,
|
|
||||||
"height": 1024,
|
|
||||||
"seed": 4,
|
|
||||||
"prompt": "local"
|
|
||||||
},
|
|
||||||
"panel_002_003": {
|
|
||||||
"url": "file:///tmp/comic-gui-local-panels-1597088181/panel_009_panel_002_003.png",
|
|
||||||
"width": 1024,
|
|
||||||
"height": 1024,
|
|
||||||
"seed": 9,
|
|
||||||
"prompt": "local"
|
|
||||||
},
|
|
||||||
"panel_001_003": {
|
|
||||||
"url": "file:///tmp/comic-gui-local-panels-1597088181/panel_003_panel_001_003.png",
|
|
||||||
"width": 1024,
|
|
||||||
"height": 1024,
|
|
||||||
"seed": 3,
|
|
||||||
"prompt": "local"
|
|
||||||
},
|
|
||||||
"panel_002_004": {
|
|
||||||
"url": "file:///tmp/comic-gui-local-panels-1597088181/panel_010_panel_002_004.png",
|
|
||||||
"width": 1024,
|
|
||||||
"height": 1024,
|
|
||||||
"seed": 10,
|
|
||||||
"prompt": "local"
|
|
||||||
},
|
|
||||||
"panel_002_002": {
|
|
||||||
"url": "file:///tmp/comic-gui-local-panels-1597088181/panel_008_panel_002_002.png",
|
|
||||||
"width": 1024,
|
|
||||||
"height": 1024,
|
|
||||||
"seed": 8,
|
|
||||||
"prompt": "local"
|
|
||||||
},
|
|
||||||
"panel_001_005": {
|
|
||||||
"url": "file:///tmp/comic-gui-local-panels-1597088181/panel_005_panel_001_005.png",
|
|
||||||
"width": 1024,
|
|
||||||
"height": 1024,
|
|
||||||
"seed": 5,
|
|
||||||
"prompt": "local"
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
"panel_errors": {
|
"panel_errors": {
|
||||||
|
|
||||||
|
|||||||
@ -130,6 +130,55 @@ action_layout_auto :: proc(controller: ^ui.App_Controller) -> string {
|
|||||||
return "Auto layout generated"
|
return "Auto layout generated"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
action_regenerate_page_layout :: proc(controller: ^ui.App_Controller, page_num: int) -> string {
|
||||||
|
if page_num < 0 || page_num >= len(controller.state.page_layouts) {
|
||||||
|
return "Invalid layout page"
|
||||||
|
}
|
||||||
|
|
||||||
|
layout := &controller.state.page_layouts[page_num]
|
||||||
|
panel_count := len(layout.panels)
|
||||||
|
if panel_count == 0 {
|
||||||
|
return "Page has no panels"
|
||||||
|
}
|
||||||
|
|
||||||
|
// For a simple visible regenerate effect, pick a random pattern that fits
|
||||||
|
pattern_ids := []string{
|
||||||
|
"single-splash", "grid-2x2", "grid-3x3", "dynamic-action",
|
||||||
|
"manga-spread", "dialogue-heavy", "cinematic-widescreen",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just pick the next valid pattern in the list (or wrap around)
|
||||||
|
start_idx := 0
|
||||||
|
for id, i in pattern_ids {
|
||||||
|
if id == layout.pattern_id {
|
||||||
|
start_idx = i
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
new_pattern_id := layout.pattern_id
|
||||||
|
for i in 1..=len(pattern_ids) {
|
||||||
|
idx := (start_idx + i) % len(pattern_ids)
|
||||||
|
candidate := pattern_ids[idx]
|
||||||
|
if core.pattern_max_panels(candidate) >= panel_count {
|
||||||
|
new_pattern_id = candidate
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pattern := core.get_layout_pattern_by_id(new_pattern_id)
|
||||||
|
layout.pattern_id = new_pattern_id
|
||||||
|
for i in 0..<panel_count {
|
||||||
|
cell_index := i
|
||||||
|
if cell_index >= len(pattern.cells) {
|
||||||
|
cell_index = len(pattern.cells) - 1
|
||||||
|
}
|
||||||
|
layout.panels[i].layout_cell = pattern.cells[cell_index]
|
||||||
|
}
|
||||||
|
|
||||||
|
return "Layout page regenerated"
|
||||||
|
}
|
||||||
|
|
||||||
export_format_name :: proc(f: core.Export_Format) -> string {
|
export_format_name :: proc(f: core.Export_Format) -> string {
|
||||||
switch f {
|
switch f {
|
||||||
case .PDF: return "PDF"
|
case .PDF: return "PDF"
|
||||||
|
|||||||
@ -331,6 +331,26 @@ run_gui_app :: proc(state: ^core.Comic_State) -> shared.App_Error {
|
|||||||
push_status(&status_msg, &action_log, fmt.tprintf("Viewing panel %d/%d", summary_opts.panel_cursor+1, panel_count))
|
push_status(&status_msg, &action_log, fmt.tprintf("Viewing panel %d/%d", summary_opts.panel_cursor+1, panel_count))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if controller.active_screen == .Layout && button_clicked(summary_prev_btn) {
|
||||||
|
layout_page_count := len(controller.state.page_layouts)
|
||||||
|
if layout_page_count > 0 {
|
||||||
|
summary_opts.layout_page_cursor -= 1
|
||||||
|
if summary_opts.layout_page_cursor < 0 {
|
||||||
|
summary_opts.layout_page_cursor = layout_page_count - 1
|
||||||
|
}
|
||||||
|
push_status(&status_msg, &action_log, fmt.tprintf("Viewing layout page %d/%d", summary_opts.layout_page_cursor+1, layout_page_count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if controller.active_screen == .Layout && button_clicked(summary_next_btn) {
|
||||||
|
layout_page_count := len(controller.state.page_layouts)
|
||||||
|
if layout_page_count > 0 {
|
||||||
|
summary_opts.layout_page_cursor += 1
|
||||||
|
if summary_opts.layout_page_cursor >= layout_page_count {
|
||||||
|
summary_opts.layout_page_cursor = 0
|
||||||
|
}
|
||||||
|
push_status(&status_msg, &action_log, fmt.tprintf("Viewing layout page %d/%d", summary_opts.layout_page_cursor+1, layout_page_count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if button_clicked(new_btn) {
|
if button_clicked(new_btn) {
|
||||||
if is_dirty && !shift_down {
|
if is_dirty && !shift_down {
|
||||||
@ -519,6 +539,26 @@ run_gui_app :: proc(state: ^core.Comic_State) -> shared.App_Error {
|
|||||||
push_status(&status_msg, &action_log, fmt.tprintf("Viewing panel %d/%d", summary_opts.panel_cursor+1, panel_count))
|
push_status(&status_msg, &action_log, fmt.tprintf("Viewing panel %d/%d", summary_opts.panel_cursor+1, panel_count))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if controller.active_screen == .Layout && ctrl_down && rl.IsKeyPressed(.LEFT_BRACKET) {
|
||||||
|
layout_page_count := len(controller.state.page_layouts)
|
||||||
|
if layout_page_count > 0 {
|
||||||
|
summary_opts.layout_page_cursor -= 1
|
||||||
|
if summary_opts.layout_page_cursor < 0 {
|
||||||
|
summary_opts.layout_page_cursor = layout_page_count - 1
|
||||||
|
}
|
||||||
|
push_status(&status_msg, &action_log, fmt.tprintf("Viewing layout page %d/%d", summary_opts.layout_page_cursor+1, layout_page_count))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if controller.active_screen == .Layout && ctrl_down && rl.IsKeyPressed(.RIGHT_BRACKET) {
|
||||||
|
layout_page_count := len(controller.state.page_layouts)
|
||||||
|
if layout_page_count > 0 {
|
||||||
|
summary_opts.layout_page_cursor += 1
|
||||||
|
if summary_opts.layout_page_cursor >= layout_page_count {
|
||||||
|
summary_opts.layout_page_cursor = 0
|
||||||
|
}
|
||||||
|
push_status(&status_msg, &action_log, fmt.tprintf("Viewing layout page %d/%d", summary_opts.layout_page_cursor+1, layout_page_count))
|
||||||
|
}
|
||||||
|
}
|
||||||
if ctrl_down && rl.IsKeyPressed(.MINUS) {
|
if ctrl_down && rl.IsKeyPressed(.MINUS) {
|
||||||
push_dirty_status(&is_dirty, &status_msg, &action_log, set_autosave_interval_text(&autosave_interval_text, autosave_secs-5))
|
push_dirty_status(&is_dirty, &status_msg, &action_log, set_autosave_interval_text(&autosave_interval_text, autosave_secs-5))
|
||||||
}
|
}
|
||||||
@ -889,12 +929,12 @@ run_gui_app :: proc(state: ^core.Comic_State) -> shared.App_Error {
|
|||||||
} else if controller.active_screen == .Panels {
|
} else if controller.active_screen == .Panels {
|
||||||
draw_small_button(summary_prev_btn, "< Pn")
|
draw_small_button(summary_prev_btn, "< Pn")
|
||||||
draw_small_button(summary_next_btn, "Pn >")
|
draw_small_button(summary_next_btn, "Pn >")
|
||||||
|
} else if controller.active_screen == .Layout {
|
||||||
|
draw_small_button(summary_prev_btn, "< Ly")
|
||||||
|
draw_small_button(summary_next_btn, "Ly >")
|
||||||
}
|
}
|
||||||
if !compact_mode {
|
if !compact_mode {
|
||||||
hint_label := "Ctrl+[ / Ctrl+]"
|
hint_label := "Ctrl+[ / Ctrl+]"
|
||||||
if controller.active_screen == .Script || controller.active_screen == .Layout {
|
|
||||||
hint_label = "Ctrl+H / Ctrl+J"
|
|
||||||
}
|
|
||||||
draw_hint_pill(rl.Rectangle{x = f32(282 + status_w_loop - 186), y = f32(lower_y_loop + 46), width = 172, height = 20}, hint_label, false)
|
draw_hint_pill(rl.Rectangle{x = f32(282 + status_w_loop - 186), y = f32(lower_y_loop + 46), width = 172, height = 20}, hint_label, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -935,6 +975,31 @@ run_gui_app :: proc(state: ^core.Comic_State) -> shared.App_Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
draw_text_fitted("Ctrl+[ / Ctrl+] panel nav", log_x_loop+18, lower_y_loop+8, 13, int(main_w_loop-status_w_loop-34), 7, TEXT_TERTIARY)
|
draw_text_fitted("Ctrl+[ / Ctrl+] panel nav", log_x_loop+18, lower_y_loop+8, 13, int(main_w_loop-status_w_loop-34), 7, TEXT_TERTIARY)
|
||||||
|
} else if controller.active_screen == .Layout {
|
||||||
|
regen, new_layout_cursor := draw_layout_detail_panel(controller, log_x_loop, lower_y_loop, main_w_loop-status_w_loop-2, 200, summary_opts.layout_page_cursor)
|
||||||
|
layout_page_count := len(controller.state.page_layouts)
|
||||||
|
if summary_opts.layout_page_cursor != new_layout_cursor {
|
||||||
|
summary_opts.layout_page_cursor = new_layout_cursor
|
||||||
|
push_status(&status_msg, &action_log, fmt.tprintf("Viewing layout page %d/%d", summary_opts.layout_page_cursor+1, layout_page_count))
|
||||||
|
}
|
||||||
|
if regen {
|
||||||
|
msg := action_regenerate_page_layout(&controller, summary_opts.layout_page_cursor)
|
||||||
|
push_status(&status_msg, &action_log, msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
wheel := rl.GetMouseWheelMove()
|
||||||
|
if wheel != 0 && layout_page_count > 0 {
|
||||||
|
summary_opts.layout_page_cursor -= int(wheel)
|
||||||
|
if summary_opts.layout_page_cursor < 0 {
|
||||||
|
summary_opts.layout_page_cursor = 0
|
||||||
|
}
|
||||||
|
if summary_opts.layout_page_cursor >= layout_page_count {
|
||||||
|
summary_opts.layout_page_cursor = layout_page_count - 1
|
||||||
|
}
|
||||||
|
push_status(&status_msg, &action_log, fmt.tprintf("Viewing layout page %d/%d", summary_opts.layout_page_cursor+1, layout_page_count))
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_text_fitted("Ctrl+H / Ctrl+J layout nav", log_x_loop+18, lower_y_loop+8, 13, int(main_w_loop-status_w_loop-34), 7, TEXT_TERTIARY)
|
||||||
} else {
|
} else {
|
||||||
draw_card(rl.Rectangle{x = f32(log_x_loop), y = f32(lower_y_loop), width = f32(main_w_loop-status_w_loop-2), height = 200})
|
draw_card(rl.Rectangle{x = f32(log_x_loop), y = f32(lower_y_loop), width = f32(main_w_loop-status_w_loop-2), height = 200})
|
||||||
draw_section_title(log_x_loop+18, lower_y_loop+6, "Action Log")
|
draw_section_title(log_x_loop+18, lower_y_loop+6, "Action Log")
|
||||||
|
|||||||
@ -441,3 +441,137 @@ draw_panels_detail_panel :: proc(controller: ui.App_Controller, x, y, w, h: i32,
|
|||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
clamp_layout_cursor :: proc(layout_count, cursor: int) -> int {
|
||||||
|
if layout_count <= 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if cursor < 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if cursor >= layout_count {
|
||||||
|
return layout_count - 1
|
||||||
|
}
|
||||||
|
return cursor
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_layout_detail_panel :: proc(controller: ui.App_Controller, x, y, w, h: i32, cursor: int) -> (regen_clicked: bool, new_cursor: int) {
|
||||||
|
new_cursor = cursor
|
||||||
|
regen_clicked = false
|
||||||
|
|
||||||
|
draw_card(rl.Rectangle{x = f32(x), y = f32(y), width = f32(w), height = f32(h)})
|
||||||
|
draw_section_title(x+18, y+6, "Layout Detail")
|
||||||
|
draw_subtle_strip(rl.Rectangle{x = f32(x+12), y = f32(y), width = f32(w-24), height = 34})
|
||||||
|
layout_count := len(controller.state.page_layouts)
|
||||||
|
if layout_count == 0 {
|
||||||
|
draw_summary_line(x+18, y+46, "No layouts yet. Use Layout Auto after panels.", SUMMARY_HINT)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
idx := clamp_layout_cursor(layout_count, cursor)
|
||||||
|
layout := controller.state.page_layouts[idx]
|
||||||
|
|
||||||
|
// Header line with status
|
||||||
|
draw_summary_line(x+18, y+46, fmt.tprintf("Page %d/%d • pattern: %s • %d panels", idx+1, layout_count, layout.pattern_id, len(layout.panels)), SUMMARY_ACCENT)
|
||||||
|
|
||||||
|
// Regenerate button
|
||||||
|
btn_rec := rl.Rectangle{x = f32(x + w - 100), y = f32(y+42), width = 80, height = 24}
|
||||||
|
draw_small_button_state(btn_rec, "Regen", true)
|
||||||
|
if button_clicked(btn_rec) {
|
||||||
|
regen_clicked = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// Layout dimensions
|
||||||
|
draw_summary_subline(x+18, y+68, fmt.tprintf("size: %d x %d", layout.width, layout.height), SUMMARY_SUBLINE)
|
||||||
|
|
||||||
|
// ── Mini wireframe preview ─────────────────────────────────
|
||||||
|
preview_x := x + 18
|
||||||
|
preview_y := y + 88
|
||||||
|
preview_max_w: f32 = f32(w) * 0.4
|
||||||
|
preview_max_h: f32 = f32(h - 100)
|
||||||
|
if preview_max_h < 40 {
|
||||||
|
preview_max_h = 40
|
||||||
|
}
|
||||||
|
|
||||||
|
// Scale to fit
|
||||||
|
lw := f32(layout.width)
|
||||||
|
lh := f32(layout.height)
|
||||||
|
if lw < 1 { lw = 1 }
|
||||||
|
if lh < 1 { lh = 1 }
|
||||||
|
scale_x := preview_max_w / lw
|
||||||
|
scale_y := preview_max_h / lh
|
||||||
|
scale := scale_x
|
||||||
|
if scale_y < scale {
|
||||||
|
scale = scale_y
|
||||||
|
}
|
||||||
|
pw := lw * scale
|
||||||
|
ph := lh * scale
|
||||||
|
|
||||||
|
// Draw page outline
|
||||||
|
page_rec := rl.Rectangle{x = f32(preview_x), y = f32(preview_y), width = pw, height = ph}
|
||||||
|
rl.DrawRectangleRounded(page_rec, 0.02, 4, BG_STRIP)
|
||||||
|
rl.DrawRectangleRoundedLinesEx(page_rec, 0.02, 4, 1.0, BORDER_CARD)
|
||||||
|
|
||||||
|
// Draw each panel cell
|
||||||
|
for i in 0..<len(layout.panels) {
|
||||||
|
cell := layout.panels[i].layout_cell
|
||||||
|
cx := f32(preview_x) + cell.x * pw
|
||||||
|
cy := f32(preview_y) + cell.y * ph
|
||||||
|
cw := cell.w * pw
|
||||||
|
ch := cell.h * ph
|
||||||
|
// Inset slightly for gutter
|
||||||
|
inset: f32 = 1.5
|
||||||
|
cell_rec := rl.Rectangle{x = cx + inset, y = cy + inset, width = cw - inset*2, height = ch - inset*2}
|
||||||
|
rl.DrawRectangleRounded(cell_rec, 0.06, 4, ACCENT_SURFACE)
|
||||||
|
rl.DrawRectangleRoundedLinesEx(cell_rec, 0.06, 4, 1.0, ACCENT_MUTED)
|
||||||
|
// Panel number label
|
||||||
|
num_label := fmt.tprintf("%d", layout.panels[i].panel_number)
|
||||||
|
label_sz: i32 = 10
|
||||||
|
if cw > 30 && ch > 14 {
|
||||||
|
draw_text_fitted(num_label, i32(cx + inset + 3), i32(cy + inset + 2), label_sz, int(cw - inset*2 - 4), 5, TEXT_SECONDARY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ── Page list (right side) ─────────────────────────────────
|
||||||
|
list_x := x + i32(preview_max_w) + 36
|
||||||
|
list_y := y + 88
|
||||||
|
list_w := w - i32(preview_max_w) - 54
|
||||||
|
row_h: i32 = 18
|
||||||
|
rows: i32 = (h - 100) / row_h
|
||||||
|
if rows < 1 {
|
||||||
|
rows = 1
|
||||||
|
}
|
||||||
|
start := idx - int(rows/2)
|
||||||
|
if start < 0 {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
end := start + int(rows)
|
||||||
|
if end > layout_count {
|
||||||
|
end = layout_count
|
||||||
|
start = end - int(rows)
|
||||||
|
if start < 0 {
|
||||||
|
start = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
line: i32 = 0
|
||||||
|
for i in start..<end {
|
||||||
|
l := controller.state.page_layouts[i]
|
||||||
|
mark := " "
|
||||||
|
row_color := TEXT_TERTIARY
|
||||||
|
if i == idx {
|
||||||
|
mark = ">"
|
||||||
|
row_color = TEXT_PRIMARY
|
||||||
|
}
|
||||||
|
|
||||||
|
row_rec := rl.Rectangle{x = f32(list_x), y = f32(list_y + line*row_h), width = f32(list_w), height = f32(row_h)}
|
||||||
|
if rl.CheckCollisionPointRec(rl.GetMousePosition(), row_rec) && rl.IsMouseButtonPressed(.LEFT) {
|
||||||
|
new_cursor = i
|
||||||
|
}
|
||||||
|
if rl.CheckCollisionPointRec(rl.GetMousePosition(), row_rec) && row_color == TEXT_TERTIARY {
|
||||||
|
row_color = TEXT_SECONDARY
|
||||||
|
}
|
||||||
|
|
||||||
|
draw_summary_subline(list_x, list_y+line*row_h+2, fit_text_for_width(fmt.tprintf("%s %02d %s (%d)", mark, l.page_number, l.pattern_id, len(l.panels)), int(list_w), 7), row_color)
|
||||||
|
line += 1
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user