Tool windows redesign and the Novel Editor workspace
Tool windows: - Every editor (scenario, scene builder, map, interactive objects, lighting, catalog export) is rebuilt on the interface ToolWindow chrome and the denser ToolTheme: header with title/close, status bar, Ctrl+S, dirty-state confirmation. - Shared building blocks in src/Common: ToolUi (rows, buttons, sections), CollapsibleSection, FilteredList, ModFileSelector, DragHandle and BackgroundCanvas (scene background with pan/zoom). - The map, interactive and light editors draw on canvases (MapEditorCanvas, InteractiveEditorCanvas, LightEditorCanvas) instead of one Control "handle" node per object; the *Handle classes are removed. - Scenario editor: tab data model (TabData), automatic graph layout (ScenarioGraphLayout), file tree/info/thumbnail panel updates. - Services: ModFileService (mod file listing and writing), SceneBackgroundService; MapFileService/ModResourceService follow. - Catalog export: per-language name/description editing. - Locale (en/ru) regrouped under tools/*; unused keys removed. Novel Editor: - collections/novel_editor.json: a "game" whose entry scene is an authoring workspace over the novel engine and the story content. - NovelEditorHub (scenes/novel_editor_hub.tscn) lists every installed tool through IToolProvider.AddMenuButton; ToolsInitializer registers it as the logical scene "novel_editor".
This commit is contained in:
1 parent
578ba7c5d5
commit
8742aa3da5
45 files changed
+4718
-3463
No files matched your search
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using Cthangover.Core.UI.Theming;
|
||||
using Cthangover.Tools.Common;
|
||||
using Godot;
|
||||
|
||||
namespace Cthangover.Core.UI.Tool.LightEditor
|
||||
{
|
||||
/// <summary>
|
||||
/// Night-time preview of a scene with its static lights. The background is
|
||||
/// drawn through the game's <c>timed_sprite</c> shader (set as this
|
||||
/// control's material), so the preview lights the picture exactly like the
|
||||
/// game; the lights' positions are pushed to the shader in this canvas'
|
||||
/// coordinates and their radii scaled from the 1920-px game width to the
|
||||
/// drawn background width. Radius rings and handles live in a separate
|
||||
/// child overlay — anything drawn by this control itself would be shaded.
|
||||
/// <para/>
|
||||
/// A click on a handle selects that light and drags it; a double-click on
|
||||
/// empty background adds a light there.
|
||||
/// </summary>
|
||||
public partial class LightEditorCanvas : BackgroundCanvas
|
||||
{
|
||||
private readonly Control _overlay;
|
||||
|
||||
/// <summary>Data being edited; set once by the window.</summary>
|
||||
public LightEditorController Controller;
|
||||
|
||||
/// <summary>Preview material (may be null when the shader is missing).</summary>
|
||||
public ShaderMaterial PreviewMaterial
|
||||
{
|
||||
get => Material as ShaderMaterial;
|
||||
set => Material = value;
|
||||
}
|
||||
|
||||
/// <summary>Raised when a light moved or was added on the canvas.</summary>
|
||||
public event Action Changed;
|
||||
|
||||
/// <summary>Raised when the selection changed on the canvas.</summary>
|
||||
public event Action SelectionChanged;
|
||||
|
||||
public LightEditorCanvas()
|
||||
{
|
||||
_overlay = new Control { MouseFilter = MouseFilterEnum.Ignore };
|
||||
AddChild(_overlay);
|
||||
_overlay.SetAnchorsAndOffsetsPreset(LayoutPreset.FullRect);
|
||||
_overlay.Connect("draw", Callable.From((Action)DrawRings));
|
||||
GuiInput += OnGuiInput;
|
||||
}
|
||||
|
||||
/// <summary>One handle per light, coloured like the light.</summary>
|
||||
public override void RebuildHandles()
|
||||
{
|
||||
ClearHandles();
|
||||
if (Controller == null || GetBackgroundRect().Size.X <= 0f)
|
||||
return;
|
||||
|
||||
for (var i = 0; i < Controller.Lights.Count; i++)
|
||||
{
|
||||
var index = i;
|
||||
var light = Controller.Lights[i];
|
||||
var handle = AddHandle(new Vector2(light.X, light.Y), light.ToColor(), i, p =>
|
||||
{
|
||||
light.X = p.X;
|
||||
light.Y = p.Y;
|
||||
Refresh();
|
||||
Changed?.Invoke();
|
||||
}, index == Controller.SelectedIndex ? 20f : 16f);
|
||||
handle.Clicked = () => SelectLight(index);
|
||||
}
|
||||
|
||||
Refresh();
|
||||
}
|
||||
|
||||
/// <summary>Redraws the preview and re-uploads the shader uniforms.</summary>
|
||||
public void Refresh()
|
||||
{
|
||||
QueueRedraw();
|
||||
_overlay.QueueRedraw();
|
||||
}
|
||||
|
||||
private void SelectLight(int index)
|
||||
{
|
||||
if (index == Controller.SelectedIndex)
|
||||
return;
|
||||
|
||||
Controller.Select(index);
|
||||
SelectionChanged?.Invoke();
|
||||
CallDeferred(nameof(RebuildHandles));
|
||||
}
|
||||
|
||||
private void OnGuiInput(InputEvent @event)
|
||||
{
|
||||
if (Controller == null || @event is not InputEventMouseButton { Pressed: true, ButtonIndex: MouseButton.Left } mb)
|
||||
return;
|
||||
|
||||
if (mb.DoubleClick && Controller.Add(Clamp01(LocalToNorm(mb.Position))) != null)
|
||||
{
|
||||
RebuildHandles();
|
||||
Changed?.Invoke();
|
||||
SelectionChanged?.Invoke();
|
||||
}
|
||||
|
||||
AcceptEvent();
|
||||
}
|
||||
|
||||
protected override void DrawContent(Rect2 rect)
|
||||
{
|
||||
// The shader compares against world_pos (MODEL_MATRIX * VERTEX), i.e.
|
||||
// canvas coordinates of the window, not this control's local ones.
|
||||
Controller?.UpdatePreview(PreviewMaterial, n => GlobalPosition + NormToLocal(n), rect.Size.X / LightEditorController.GameWidth);
|
||||
}
|
||||
|
||||
private void DrawRings()
|
||||
{
|
||||
var rect = GetBackgroundRect();
|
||||
if (Controller == null || rect.Size.X <= 0f)
|
||||
return;
|
||||
|
||||
var scale = rect.Size.X / LightEditorController.GameWidth;
|
||||
for (var i = 0; i < Controller.Lights.Count; i++)
|
||||
{
|
||||
var light = Controller.Lights[i];
|
||||
var selected = i == Controller.SelectedIndex;
|
||||
var centre = NormToLocal(new Vector2(light.X, light.Y));
|
||||
var color = selected ? UiTokens.AccentBright : UiTokens.Alpha(light.ToColor(), 0.45f);
|
||||
_overlay.DrawArc(centre, light.Radius * scale, 0f, Mathf.Tau, 64, color, selected ? 1.5f : 1f, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user