From 8cce1866b10143638f3ffc61ff112a748e234238 Mon Sep 17 00:00:00 2001 From: Gabriel Kaszewski Date: Thu, 19 Mar 2026 03:32:40 +0100 Subject: [PATCH] feat: display settings screen with window mode and resolution picker --- objects/ui/display_settings.tscn | 77 +++++++++++++++++ objects/ui/settings_menu.tscn | 1 - scenes/main_menu.tscn | 10 +-- scripts/UI/DisplaySettings.cs | 144 +++++++++++++++++++++++++++++++ 4 files changed, 225 insertions(+), 7 deletions(-) create mode 100644 objects/ui/display_settings.tscn create mode 100644 scripts/UI/DisplaySettings.cs diff --git a/objects/ui/display_settings.tscn b/objects/ui/display_settings.tscn new file mode 100644 index 0000000..8e20121 --- /dev/null +++ b/objects/ui/display_settings.tscn @@ -0,0 +1,77 @@ +[gd_scene load_steps=3 format=3 uid="uid://display_settings_scene"] + +[ext_resource type="Script" uid="uid://display_settings_script" path="res://scripts/UI/DisplaySettings.cs" id="1_dispset"] + +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dispset"] +bg_color = Color(0, 0, 0, 1) + +[node name="Display settings" type="Control" node_paths=PackedStringArray("WindowModeButton", "ResolutionButton", "DisplaySettingsControl")] +layout_mode = 3 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +size_flags_horizontal = 6 +size_flags_vertical = 6 +script = ExtResource("1_dispset") +WindowModeButton = NodePath("PanelContainer/MarginContainer/VBoxContainer/Window Mode/WindowModeButton") +ResolutionButton = NodePath("PanelContainer/MarginContainer/VBoxContainer/Resolution/ResolutionButton") +DisplaySettingsControl = NodePath(".") + +[node name="PanelContainer" type="PanelContainer" parent="."] +layout_mode = 1 +anchors_preset = 15 +anchor_right = 1.0 +anchor_bottom = 1.0 +grow_horizontal = 2 +grow_vertical = 2 +theme_override_styles/panel = SubResource("StyleBoxFlat_dispset") + +[node name="MarginContainer" type="MarginContainer" parent="PanelContainer"] +layout_mode = 2 +theme_override_constants/margin_left = 8 +theme_override_constants/margin_top = 8 +theme_override_constants/margin_right = 8 +theme_override_constants/margin_bottom = 8 + +[node name="VBoxContainer" type="VBoxContainer" parent="PanelContainer/MarginContainer"] +layout_mode = 2 +size_flags_horizontal = 4 +size_flags_vertical = 4 + +[node name="Display" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer"] +layout_mode = 2 +text = "DISPLAY" +horizontal_alignment = 1 +vertical_alignment = 1 +uppercase = true + +[node name="Spacer" type="Control" parent="PanelContainer/MarginContainer/VBoxContainer"] +custom_minimum_size = Vector2(0, 32) +layout_mode = 2 +size_flags_vertical = 3 + +[node name="Window Mode" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_constants/separation = 4 + +[node name="Label" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/Window Mode"] +layout_mode = 2 +text = "WINDOW_MODE" +horizontal_alignment = 1 + +[node name="WindowModeButton" type="OptionButton" parent="PanelContainer/MarginContainer/VBoxContainer/Window Mode"] +layout_mode = 2 + +[node name="Resolution" type="VBoxContainer" parent="PanelContainer/MarginContainer/VBoxContainer"] +layout_mode = 2 +theme_override_constants/separation = 4 + +[node name="Label" type="Label" parent="PanelContainer/MarginContainer/VBoxContainer/Resolution"] +layout_mode = 2 +text = "RESOLUTION" +horizontal_alignment = 1 + +[node name="ResolutionButton" type="OptionButton" parent="PanelContainer/MarginContainer/VBoxContainer/Resolution"] +layout_mode = 2 diff --git a/objects/ui/settings_menu.tscn b/objects/ui/settings_menu.tscn index 2407a9a..f2fb6ef 100644 --- a/objects/ui/settings_menu.tscn +++ b/objects/ui/settings_menu.tscn @@ -59,7 +59,6 @@ flat = true [node name="Display Settings Button" type="Button" parent="PanelContainer/MarginContainer/VBoxContainer"] layout_mode = 2 -disabled = true text = "DISPLAY_BUTTON" flat = true diff --git a/scenes/main_menu.tscn b/scenes/main_menu.tscn index 35245bf..af2e1ff 100644 --- a/scenes/main_menu.tscn +++ b/scenes/main_menu.tscn @@ -1,10 +1,11 @@ -[gd_scene load_steps=6 format=3 uid="uid://cl00e2ocomk3m"] +[gd_scene load_steps=7 format=3 uid="uid://cl00e2ocomk3m"] [ext_resource type="PackedScene" uid="uid://8b6ol5sssbgo" path="res://objects/ui/main_menu.tscn" id="1_ekxnf"] [ext_resource type="PackedScene" uid="uid://y0ae6e7t70fj" path="res://objects/ui/settings_menu.tscn" id="2_bqqt6"] [ext_resource type="PackedScene" uid="uid://bwgmrcyj4mvu" path="res://objects/ui/credits.tscn" id="3_bqqt6"] [ext_resource type="PackedScene" uid="uid://b5fx1vdfky307" path="res://objects/ui/audio_settings.tscn" id="4_8ln24"] [ext_resource type="PackedScene" uid="uid://cvfsbiy5ggrpg" path="res://objects/ui/input_settings.tscn" id="5_rtw2f"] +[ext_resource type="PackedScene" uid="uid://display_settings_scene" path="res://objects/ui/display_settings.tscn" id="6_dispset"] [node name="Main menu" type="CanvasLayer"] @@ -34,8 +35,5 @@ anchors_preset = 0 offset_right = 40.0 offset_bottom = 40.0 -[node name="Display Settings" type="Control" parent="."] -layout_mode = 3 -anchors_preset = 0 -offset_right = 40.0 -offset_bottom = 40.0 +[node name="Display Settings" parent="." instance=ExtResource("6_dispset")] +visible = false diff --git a/scripts/UI/DisplaySettings.cs b/scripts/UI/DisplaySettings.cs new file mode 100644 index 0000000..374581d --- /dev/null +++ b/scripts/UI/DisplaySettings.cs @@ -0,0 +1,144 @@ +using Godot; +using Mr.BrickAdventures.Autoloads; + +namespace Mr.BrickAdventures.scripts.UI; + +public partial class DisplaySettings : Control +{ + [Export] public OptionButton WindowModeButton { get; set; } + [Export] public OptionButton ResolutionButton { get; set; } + [Export] public Control DisplaySettingsControl { get; set; } + + private UIManager UIManager => UIManager.Instance; + + public override void _Ready() + { + PopulateWindowMode(); + PopulateResolutions(); + + WindowModeButton.ItemSelected += OnWindowModeChanged; + ResolutionButton.ItemSelected += OnResolutionChanged; + + LoadSettings(); + + if (InputDeviceManager.Instance != null) + InputDeviceManager.Instance.DeviceChanged += OnDeviceChanged; + DisplaySettingsControl.VisibilityChanged += OnVisibilityChanged; + } + + public override void _ExitTree() + { + if (InputDeviceManager.Instance != null) + InputDeviceManager.Instance.DeviceChanged -= OnDeviceChanged; + DisplaySettingsControl.VisibilityChanged -= OnVisibilityChanged; + SettingsManager.Instance?.SaveDisplaySettings(); + } + + public override void _UnhandledInput(InputEvent @event) + { + if (!@event.IsActionReleased("ui_cancel")) return; + if (!UIManager.IsScreenOnTop(DisplaySettingsControl)) return; + + SettingsManager.Instance?.SaveDisplaySettings(); + UIManager.PopScreen(); + } + + private void OnVisibilityChanged() + { + if (DisplaySettingsControl.IsVisibleInTree() && InputDeviceManager.Instance?.IsPointerless == true) + GrabFirstFocus(); + } + + private void OnDeviceChanged(int device) + { + var d = (InputDeviceManager.InputDevice)device; + if (d != InputDeviceManager.InputDevice.Mouse && DisplaySettingsControl.IsVisibleInTree()) + GrabFirstFocus(); + } + + private void GrabFirstFocus() => WindowModeButton.GrabFocus(); + + private void PopulateWindowMode() + { + WindowModeButton.Clear(); + WindowModeButton.AddItem("Fullscreen", 0); + WindowModeButton.AddItem("Borderless", 1); + WindowModeButton.AddItem("Windowed", 2); + } + + private void PopulateResolutions() + { + ResolutionButton.Clear(); + foreach (var res in SettingsManager.Instance.GetAllResolutions()) + { + string label = $"{res.X}×{res.Y}"; + ResolutionButton.AddItem(label); + } + } + + private void OnWindowModeChanged(long idx) + { + string mode = idx switch + { + 0 => "fullscreen", + 1 => "borderless", + _ => "windowed" + }; + + ApplyWindowMode(mode); + SettingsManager.Instance.WindowMode = mode; + ResolutionButton.Disabled = mode == "fullscreen"; + SettingsManager.Instance.SaveDisplaySettings(); + } + + private void OnResolutionChanged(long idx) + { + var resolutions = SettingsManager.Instance.GetAllResolutions(); + if (idx < 0 || idx >= resolutions.Count) return; + + var res = resolutions[(int)idx]; + SettingsManager.Instance.Resolution = res; + + var mode = SettingsManager.Instance.WindowMode; + if (mode != "fullscreen") + DisplayServer.WindowSetSize(res); + + SettingsManager.Instance.SaveDisplaySettings(); + } + + private static void ApplyWindowMode(string mode) + { + switch (mode) + { + case "fullscreen": + DisplayServer.WindowSetMode(DisplayServer.WindowMode.Fullscreen); + break; + case "borderless": + DisplayServer.WindowSetMode(DisplayServer.WindowMode.Windowed); + DisplayServer.WindowSetFlag(DisplayServer.WindowFlags.Borderless, true); + break; + default: + DisplayServer.WindowSetMode(DisplayServer.WindowMode.Windowed); + DisplayServer.WindowSetFlag(DisplayServer.WindowFlags.Borderless, false); + break; + } + } + + private void LoadSettings() + { + var mode = SettingsManager.Instance.WindowMode; + WindowModeButton.Selected = mode switch + { + "fullscreen" => 0, + "borderless" => 1, + _ => 2 + }; + ResolutionButton.Disabled = mode == "fullscreen"; + + var resolution = SettingsManager.Instance.Resolution; + var resolutions = SettingsManager.Instance.GetAllResolutions(); + int resIdx = resolutions.IndexOf(resolution); + if (resIdx >= 0) + ResolutionButton.Selected = resIdx; + } +}