Polish title online and save slot interfaces
This commit is contained in:
parent
a3aea98982
commit
867fd431af
8 changed files with 463 additions and 86 deletions
|
|
@ -30,8 +30,8 @@ enabled=PackedStringArray("res://addons/netfishing_shoreline_baker/plugin.cfg")
|
|||
|
||||
[gui]
|
||||
|
||||
theme/custom="res://ui/game_theme.tres"
|
||||
timers/tooltip_delay_sec=0.0
|
||||
theme/custom="res://ui/game_theme.tres"
|
||||
|
||||
[input]
|
||||
|
||||
|
|
|
|||
|
|
@ -130,7 +130,28 @@ func _validate_primary_menu_navigation() -> void:
|
|||
and not (title.get_node("%DeleteSaveButton") as Control).visible,
|
||||
"Legacy New/Delete title bubbles are still visible.",
|
||||
)
|
||||
_assert_directionally_reachable(title_controls.front(), title_controls)
|
||||
_assert_neighbor(
|
||||
title.get_node("%JoinGameButton") as Control,
|
||||
&"focus_neighbor_right",
|
||||
play_button,
|
||||
)
|
||||
_assert_neighbor(
|
||||
title.get_node("%SettingsButton") as Control,
|
||||
&"focus_neighbor_left",
|
||||
play_button,
|
||||
)
|
||||
_assert_neighbor(
|
||||
title.get_node("%CreditsButton") as Control,
|
||||
&"focus_neighbor_right",
|
||||
play_button,
|
||||
)
|
||||
_assert_neighbor(
|
||||
title.get_node("%QuitButton") as Control,
|
||||
&"focus_neighbor_left",
|
||||
play_button,
|
||||
)
|
||||
for title_control: Control in title_controls:
|
||||
_assert_directionally_reachable(title_control, title_controls)
|
||||
title.queue_free()
|
||||
await process_frame
|
||||
|
||||
|
|
@ -195,6 +216,13 @@ func _validate_save_slots_navigation() -> void:
|
|||
page.call("_select_page", &"saves", false)
|
||||
await process_frame
|
||||
var import_button := page.get_node("%ImportSlotButton") as Button
|
||||
var actions := page.get_node("%Actions") as VBoxContainer
|
||||
var secondary_actions := page.get_node("%SecondaryActions") as GridContainer
|
||||
var play_slot := page.get_node("%PlaySlotButton") as Button
|
||||
var rename_slot := page.get_node("%RenameSlotButton") as Button
|
||||
var duplicate_slot := page.get_node("%DuplicateSlotButton") as Button
|
||||
var export_slot := page.get_node("%ExportSlotButton") as Button
|
||||
var delete_slot := page.get_node("%DeleteSlotButton") as Button
|
||||
_expect(
|
||||
saves_tab.find_valid_focus_neighbor(SIDE_BOTTOM) == import_button,
|
||||
"An empty Save Slots page does not lead from its tab to Import Save.",
|
||||
|
|
@ -204,6 +232,29 @@ func _validate_save_slots_navigation() -> void:
|
|||
== page.get_node("%BackButton"),
|
||||
"An empty Save Slots page does not lead from Import Save to Back.",
|
||||
)
|
||||
_expect(
|
||||
is_equal_approx(play_slot.size.x, actions.size.x),
|
||||
"Save-slot Play does not span the full action width.",
|
||||
)
|
||||
_expect(
|
||||
secondary_actions.columns == 2
|
||||
and is_equal_approx(rename_slot.size.x, duplicate_slot.size.x)
|
||||
and is_equal_approx(export_slot.size.x, delete_slot.size.x)
|
||||
and is_equal_approx(rename_slot.position.y, duplicate_slot.position.y)
|
||||
and is_equal_approx(export_slot.position.y, delete_slot.position.y)
|
||||
and export_slot.position.y > rename_slot.position.y,
|
||||
"Save-slot secondary actions are not arranged as a 2 by 2 grid.",
|
||||
)
|
||||
var play_style := play_slot.get_theme_stylebox("normal") as StyleBoxFlat
|
||||
_expect(
|
||||
play_style != null and play_style.bg_color == UtilityPageStyle.GREEN,
|
||||
"Save-slot Play does not use the green primary-action style.",
|
||||
)
|
||||
_assert_neighbor(play_slot, &"focus_neighbor_bottom", rename_slot)
|
||||
_assert_neighbor(rename_slot, &"focus_neighbor_right", duplicate_slot)
|
||||
_assert_neighbor(rename_slot, &"focus_neighbor_bottom", export_slot)
|
||||
_assert_neighbor(duplicate_slot, &"focus_neighbor_bottom", delete_slot)
|
||||
_assert_neighbor(export_slot, &"focus_neighbor_right", delete_slot)
|
||||
page.queue_free()
|
||||
await process_frame
|
||||
|
||||
|
|
@ -222,6 +273,9 @@ func _validate_join_game_navigation() -> void:
|
|||
var address := page.get_node("%Address") as LineEdit
|
||||
var name_edit := page.get_node("%NameEdit") as LineEdit
|
||||
var server_list := page.get_node("%ServerList") as ItemList
|
||||
var presence := page.get_node("%PresenceButton") as Button
|
||||
var room_open := page.get_node("%RoomOpenButton") as Button
|
||||
var room_listing := page.get_node("%RoomListingButton") as Button
|
||||
var refresh := page.get_node("%RefreshButton") as Button
|
||||
var join := page.get_node("%JoinButton") as Button
|
||||
var save := page.get_node("%SaveButton") as Button
|
||||
|
|
@ -233,6 +287,7 @@ func _validate_join_game_navigation() -> void:
|
|||
var direct_content := page.get_node("%DirectContent") as Control
|
||||
var list_content := page.get_node("%ListContent") as Control
|
||||
var content_panel := page.get_node("%ContentPanel") as PanelContainer
|
||||
var actions := page.get_node("%Actions") as HBoxContainer
|
||||
var modes: Array[Control] = [discover, friends, direct, saved, recent]
|
||||
var tab_overlap: float = (
|
||||
discover.get_global_rect().end.y
|
||||
|
|
@ -265,9 +320,36 @@ func _validate_join_game_navigation() -> void:
|
|||
_assert_neighbor(refresh, &"focus_neighbor_right", join)
|
||||
_assert_neighbor(join, &"focus_neighbor_bottom", back)
|
||||
_assert_neighbor(back, &"focus_neighbor_top", join)
|
||||
_expect(
|
||||
is_equal_approx(
|
||||
join.get_global_rect().end.x,
|
||||
actions.get_global_rect().end.x,
|
||||
),
|
||||
"Discover Refresh and Join are not right-justified.",
|
||||
)
|
||||
var discover_controls: Array[Control] = modes.duplicate()
|
||||
discover_controls.append_array([server_list, refresh, join, back])
|
||||
_assert_directionally_reachable(discover, discover_controls)
|
||||
room_open.show()
|
||||
room_listing.show()
|
||||
_set_button_state(room_open, true)
|
||||
_set_button_state(room_listing, true)
|
||||
page.call("_configure_controller_navigation")
|
||||
await process_frame
|
||||
_assert_neighbor(room_open, &"focus_neighbor_right", room_listing)
|
||||
_assert_neighbor(room_open, &"focus_neighbor_left", room_open)
|
||||
_assert_neighbor(room_listing, &"focus_neighbor_left", room_open)
|
||||
_assert_neighbor(room_listing, &"focus_neighbor_bottom", server_list)
|
||||
var hosted_discover_controls: Array[Control] = modes.duplicate()
|
||||
hosted_discover_controls.append_array([
|
||||
room_open,
|
||||
room_listing,
|
||||
server_list,
|
||||
refresh,
|
||||
join,
|
||||
back,
|
||||
])
|
||||
_assert_directionally_reachable(discover, hosted_discover_controls)
|
||||
var rooms: Array[Dictionary] = [
|
||||
{
|
||||
"room_id": "controller-default-room",
|
||||
|
|
@ -293,16 +375,36 @@ func _validate_join_game_navigation() -> void:
|
|||
)
|
||||
|
||||
page.set("_mode", JoinGamePage.Mode.FRIENDS)
|
||||
(page.get_node("%OnlineControls") as Control).hide()
|
||||
presence.show()
|
||||
page.call("_configure_controller_navigation")
|
||||
await process_frame
|
||||
_assert_neighbor(friends, &"focus_neighbor_bottom", server_list)
|
||||
_assert_neighbor(server_list, &"focus_neighbor_top", friends)
|
||||
_assert_neighbor(server_list, &"focus_neighbor_bottom", presence)
|
||||
_assert_neighbor(presence, &"focus_neighbor_right", refresh)
|
||||
_assert_neighbor(refresh, &"focus_neighbor_right", join)
|
||||
_expect(
|
||||
is_equal_approx(
|
||||
presence.get_global_rect().position.x,
|
||||
actions.get_global_rect().position.x,
|
||||
),
|
||||
"Friend presence is not left-justified in the action row.",
|
||||
)
|
||||
_expect(
|
||||
is_equal_approx(
|
||||
join.get_global_rect().end.x,
|
||||
actions.get_global_rect().end.x,
|
||||
),
|
||||
"Friends Refresh and Join are not right-justified.",
|
||||
)
|
||||
var friend_controls: Array[Control] = modes.duplicate()
|
||||
friend_controls.append_array([server_list, refresh, join, back])
|
||||
friend_controls.append_array([server_list, presence, refresh, join, back])
|
||||
_assert_directionally_reachable(friends, friend_controls)
|
||||
|
||||
list_content.hide()
|
||||
direct_content.show()
|
||||
presence.hide()
|
||||
address.show()
|
||||
address.editable = true
|
||||
server_list.hide()
|
||||
|
|
|
|||
|
|
@ -32,6 +32,10 @@ const DIRECT_WORKFLOW_HELP: String = (
|
|||
@onready var _direct_content: Control = %DirectContent
|
||||
@onready var _list_content: Control = %ListContent
|
||||
@onready var _list_title: Label = %ListTitle
|
||||
@onready var _online_controls: Control = %OnlineControls
|
||||
@onready var _presence_button: Button = %PresenceButton
|
||||
@onready var _room_open_button: Button = %RoomOpenButton
|
||||
@onready var _room_listing_button: Button = %RoomListingButton
|
||||
@onready var _details_panel: PanelContainer = %DetailsPanel
|
||||
@onready var _address: LineEdit = %Address
|
||||
@onready var _address_label: Label = %AddressLabel
|
||||
|
|
@ -89,6 +93,9 @@ func _ready() -> void:
|
|||
_direct_button.pressed.connect(_set_mode.bind(Mode.DIRECT))
|
||||
_saved_button.pressed.connect(_set_mode.bind(Mode.SAVED))
|
||||
_recent_button.pressed.connect(_set_mode.bind(Mode.RECENT))
|
||||
_presence_button.pressed.connect(_on_presence_pressed)
|
||||
_room_open_button.pressed.connect(_on_room_open_pressed)
|
||||
_room_listing_button.pressed.connect(_on_room_listing_pressed)
|
||||
_refresh_button.pressed.connect(_request_discovery_refresh)
|
||||
_join_button.pressed.connect(_request_join)
|
||||
_save_button.pressed.connect(_on_save_pressed)
|
||||
|
|
@ -238,6 +245,10 @@ func setup(
|
|||
_on_peer_count_changed
|
||||
):
|
||||
_network_session.peer_count_changed.connect(_on_peer_count_changed)
|
||||
if not _network_session.host_openness_changed.is_connected(
|
||||
_on_host_openness_changed
|
||||
):
|
||||
_network_session.host_openness_changed.connect(_on_host_openness_changed)
|
||||
if (
|
||||
_saved_servers != null
|
||||
and not _saved_servers.data_changed.is_connected(_on_store_changed)
|
||||
|
|
@ -278,6 +289,22 @@ func setup(
|
|||
_discovery.social_status_changed.connect(
|
||||
_on_social_status_changed
|
||||
)
|
||||
if not _discovery.host_settings_changed.is_connected(
|
||||
_on_host_settings_changed
|
||||
):
|
||||
_discovery.host_settings_changed.connect(
|
||||
_on_host_settings_changed
|
||||
)
|
||||
if not _discovery.host_status_changed.is_connected(
|
||||
_on_host_status_changed
|
||||
):
|
||||
_discovery.host_status_changed.connect(_on_host_status_changed)
|
||||
if not _discovery.presence_sharing_changed.is_connected(
|
||||
_on_presence_sharing_changed
|
||||
):
|
||||
_discovery.presence_sharing_changed.connect(
|
||||
_on_presence_sharing_changed
|
||||
)
|
||||
_friend_entries = _discovery.get_friend_presence()
|
||||
_refresh()
|
||||
|
||||
|
|
@ -792,6 +819,7 @@ func _refresh() -> void:
|
|||
_direct_content.visible = direct_content_visible
|
||||
_list_content.visible = list_content_visible
|
||||
_list_title.text = _current_list_title()
|
||||
_refresh_online_controls(discovery_mode, friends_mode, connecting)
|
||||
_address.visible = direct or _name_entry_active
|
||||
_address_label.visible = _address.visible
|
||||
_address_helper.visible = _address.visible
|
||||
|
|
@ -916,6 +944,66 @@ func _current_list_title() -> String:
|
|||
return "public rooms"
|
||||
|
||||
|
||||
func _refresh_online_controls(
|
||||
discovery_mode: bool,
|
||||
friends_mode: bool,
|
||||
connecting: bool,
|
||||
) -> void:
|
||||
_online_controls.visible = discovery_mode and not _name_entry_active
|
||||
_presence_button.visible = friends_mode and not _name_entry_active
|
||||
var presence_enabled: bool = (
|
||||
_discovery != null and _discovery.is_presence_sharing()
|
||||
)
|
||||
_presence_button.text = (
|
||||
"friends: online" if presence_enabled else "friends: offline"
|
||||
)
|
||||
_presence_button.disabled = (
|
||||
connecting or _discovery == null or not _discovery.is_configured()
|
||||
)
|
||||
_presence_button.tooltip_text = (
|
||||
"Stay visible to friends until you switch this off. "
|
||||
+ "Your current room is shared only while it is publicly joinable."
|
||||
if presence_enabled
|
||||
else "Show as online to friends until you switch this off."
|
||||
)
|
||||
if not _online_controls.visible:
|
||||
return
|
||||
var local_host: bool = _network_session.is_host()
|
||||
_room_open_button.visible = local_host
|
||||
_room_listing_button.visible = local_host
|
||||
if not local_host:
|
||||
return
|
||||
var room_open: bool = _network_session.is_open_host()
|
||||
var room_listed: bool = (
|
||||
_discovery != null and _discovery.is_discoverable()
|
||||
)
|
||||
_room_open_button.text = "room: open" if room_open else "room: closed"
|
||||
_room_open_button.disabled = connecting
|
||||
_room_open_button.tooltip_text = (
|
||||
"Close this room to new connections."
|
||||
if room_open
|
||||
else "Open this room so other players can connect."
|
||||
)
|
||||
_room_listing_button.text = (
|
||||
"listing: on" if room_listed else "listing: off"
|
||||
)
|
||||
_room_listing_button.disabled = (
|
||||
connecting
|
||||
or _discovery == null
|
||||
or not _discovery.is_configured()
|
||||
or not room_open
|
||||
)
|
||||
_room_listing_button.tooltip_text = (
|
||||
"Remove this room from the public room browser."
|
||||
if room_listed
|
||||
else "List this open room in the public room browser."
|
||||
if room_open and _discovery != null and _discovery.is_configured()
|
||||
else "Open the room before enabling its public listing."
|
||||
if _discovery != null and _discovery.is_configured()
|
||||
else "Room discovery is not configured in this build."
|
||||
)
|
||||
|
||||
|
||||
func _configure_controller_navigation() -> void:
|
||||
var mode_buttons: Array[Control] = [
|
||||
_discover_button,
|
||||
|
|
@ -924,12 +1012,24 @@ func _configure_controller_navigation() -> void:
|
|||
_saved_button,
|
||||
_recent_button,
|
||||
]
|
||||
var online_controls: Array[Control] = []
|
||||
for control: Control in [
|
||||
_room_open_button,
|
||||
_room_listing_button,
|
||||
]:
|
||||
if _controller_focus_eligible(control):
|
||||
online_controls.append(control)
|
||||
var content_controls: Array[Control] = []
|
||||
for control: Control in [_address, _name_edit, _server_list]:
|
||||
for control: Control in [
|
||||
_address,
|
||||
_name_edit,
|
||||
_server_list,
|
||||
]:
|
||||
if _controller_focus_eligible(control):
|
||||
content_controls.append(control)
|
||||
var action_controls: Array[Control] = []
|
||||
for control: Control in [
|
||||
_presence_button,
|
||||
_refresh_button,
|
||||
_join_button,
|
||||
_save_button,
|
||||
|
|
@ -942,12 +1042,16 @@ func _configure_controller_navigation() -> void:
|
|||
action_controls.append(control)
|
||||
var all_controls: Array[Control] = []
|
||||
all_controls.append_array(mode_buttons)
|
||||
all_controls.append_array(online_controls)
|
||||
all_controls.append_array(content_controls)
|
||||
all_controls.append_array(action_controls)
|
||||
all_controls.append(_back_button)
|
||||
for control: Control in all_controls:
|
||||
control.focus_mode = Control.FOCUS_ALL
|
||||
for control: Control in [
|
||||
_presence_button,
|
||||
_room_open_button,
|
||||
_room_listing_button,
|
||||
_address,
|
||||
_name_edit,
|
||||
_server_list,
|
||||
|
|
@ -963,7 +1067,9 @@ func _configure_controller_navigation() -> void:
|
|||
control.focus_mode = Control.FOCUS_NONE
|
||||
_back_button.focus_mode = Control.FOCUS_ALL
|
||||
var primary_content: Control = (
|
||||
content_controls.front()
|
||||
online_controls.front()
|
||||
if not online_controls.is_empty()
|
||||
else content_controls.front()
|
||||
if not content_controls.is_empty()
|
||||
else action_controls.front()
|
||||
if not action_controls.is_empty()
|
||||
|
|
@ -978,11 +1084,29 @@ func _configure_controller_navigation() -> void:
|
|||
mode_button,
|
||||
primary_content,
|
||||
)
|
||||
for index: int in online_controls.size():
|
||||
var online_control: Control = online_controls[index]
|
||||
var below: Control = (
|
||||
content_controls.front()
|
||||
if not content_controls.is_empty()
|
||||
else action_controls.front()
|
||||
if not action_controls.is_empty()
|
||||
else _back_button
|
||||
)
|
||||
_set_controller_neighbors(
|
||||
online_control,
|
||||
online_controls[maxi(index - 1, 0)],
|
||||
online_controls[mini(index + 1, online_controls.size() - 1)],
|
||||
mode_buttons[int(_mode)],
|
||||
below,
|
||||
)
|
||||
for index: int in content_controls.size():
|
||||
var content: Control = content_controls[index]
|
||||
var above: Control = (
|
||||
content_controls[index - 1]
|
||||
if index > 0
|
||||
else online_controls.front()
|
||||
if not online_controls.is_empty()
|
||||
else mode_buttons[int(_mode)]
|
||||
)
|
||||
var below: Control = (
|
||||
|
|
@ -1001,6 +1125,8 @@ func _configure_controller_navigation() -> void:
|
|||
action_controls[mini(index + 1, action_controls.size() - 1)],
|
||||
content_controls.back()
|
||||
if not content_controls.is_empty()
|
||||
else online_controls.back()
|
||||
if not online_controls.is_empty()
|
||||
else mode_buttons[int(_mode)],
|
||||
_back_button,
|
||||
)
|
||||
|
|
@ -1009,6 +1135,8 @@ func _configure_controller_navigation() -> void:
|
|||
if not action_controls.is_empty()
|
||||
else content_controls.back()
|
||||
if not content_controls.is_empty()
|
||||
else online_controls.back()
|
||||
if not online_controls.is_empty()
|
||||
else mode_buttons[int(_mode)]
|
||||
)
|
||||
_set_controller_neighbors(
|
||||
|
|
@ -1270,6 +1398,76 @@ func _on_social_status_changed(message: String, is_error: bool) -> void:
|
|||
_set_status(message, is_error)
|
||||
|
||||
|
||||
func _on_presence_pressed() -> void:
|
||||
if _discovery == null:
|
||||
_set_status("Friend presence is not available.", true)
|
||||
return
|
||||
var enabling: bool = not _discovery.is_presence_sharing()
|
||||
if not _discovery.set_presence_sharing(enabling):
|
||||
return
|
||||
_set_status(
|
||||
"You will remain online to friends until you switch this off."
|
||||
if enabling
|
||||
else "You now appear offline to friends."
|
||||
)
|
||||
_refresh()
|
||||
|
||||
|
||||
func _on_room_open_pressed() -> void:
|
||||
if _network_session == null or not _network_session.is_host():
|
||||
return
|
||||
var opening: bool = not _network_session.is_open_host()
|
||||
if not _network_session.set_host_open(opening):
|
||||
_set_status("The room could not be updated.", true)
|
||||
return
|
||||
_set_status(
|
||||
"The room is open to new connections."
|
||||
if opening
|
||||
else "The room is closed to new connections."
|
||||
)
|
||||
_refresh()
|
||||
|
||||
|
||||
func _on_room_listing_pressed() -> void:
|
||||
if _discovery == null:
|
||||
return
|
||||
var enabling: bool = not _discovery.is_discoverable()
|
||||
if not _discovery.set_discoverable(enabling):
|
||||
return
|
||||
_set_status(
|
||||
"The room is being listed publicly."
|
||||
if enabling
|
||||
else "The room is no longer listed publicly."
|
||||
)
|
||||
_refresh()
|
||||
|
||||
|
||||
func _on_host_openness_changed(_is_open: bool) -> void:
|
||||
_refresh()
|
||||
|
||||
|
||||
func _on_host_settings_changed(
|
||||
_room_name: String,
|
||||
_discoverable: bool,
|
||||
) -> void:
|
||||
_refresh()
|
||||
|
||||
|
||||
func _on_host_status_changed(message: String, is_error: bool) -> void:
|
||||
if (
|
||||
_mode == Mode.DISCOVER
|
||||
and is_visible_in_tree()
|
||||
and _network_session != null
|
||||
and _network_session.is_host()
|
||||
):
|
||||
_set_status(message, is_error)
|
||||
_refresh()
|
||||
|
||||
|
||||
func _on_presence_sharing_changed(_enabled: bool) -> void:
|
||||
_refresh()
|
||||
|
||||
|
||||
func _format_result_code(result_code: String) -> String:
|
||||
match result_code.strip_edges().to_upper():
|
||||
"SUCCESS":
|
||||
|
|
|
|||
|
|
@ -138,9 +138,35 @@ layout_mode = 2
|
|||
theme_override_font_sizes/font_size = 20
|
||||
text = "public rooms"
|
||||
|
||||
[node name="OnlineControls" type="HBoxContainer" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack/ListContent"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 42)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="OnlineControlsSpacer" type="Control" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack/ListContent/OnlineControls"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="RoomOpenButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack/ListContent/OnlineControls"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(160, 42)
|
||||
layout_mode = 2
|
||||
focus_mode = 2
|
||||
text = "room: closed"
|
||||
|
||||
[node name="RoomListingButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack/ListContent/OnlineControls"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(180, 42)
|
||||
layout_mode = 2
|
||||
focus_mode = 2
|
||||
text = "listing: off"
|
||||
|
||||
[node name="ServerList" type="ItemList" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/PageStack/ListContent"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 180)
|
||||
custom_minimum_size = Vector2(0, 140)
|
||||
layout_mode = 2
|
||||
size_flags_vertical = 3
|
||||
theme_override_font_sizes/font_size = 17
|
||||
|
|
@ -235,7 +261,18 @@ unique_name_in_owner = true
|
|||
custom_minimum_size = Vector2(0, 46)
|
||||
layout_mode = 2
|
||||
theme_override_constants/separation = 10
|
||||
alignment = 1
|
||||
|
||||
[node name="PresenceButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/Actions"]
|
||||
unique_name_in_owner = true
|
||||
visible = false
|
||||
custom_minimum_size = Vector2(210, 46)
|
||||
layout_mode = 2
|
||||
focus_mode = 2
|
||||
text = "friends: offline"
|
||||
|
||||
[node name="ActionsSpacer" type="Control" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/Actions"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="RefreshButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/ContentLayout/Actions"]
|
||||
unique_name_in_owner = true
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ var _online_state_label: Label
|
|||
var _discoverable_toggle: Button
|
||||
var _discoverable_state_label: Label
|
||||
var _host_discovery_status: Label
|
||||
var _reset_artwork_button: Button
|
||||
var _current_tab := 0
|
||||
var _active: bool = false
|
||||
var _interactive: bool = false
|
||||
|
|
@ -295,6 +296,9 @@ func _build_host_settings(root: VBoxContainer) -> void:
|
|||
"font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY
|
||||
)
|
||||
access_row.add_child(_host_discovery_status)
|
||||
_reset_artwork_button = Button.new()
|
||||
_configure_session_artwork_reset_button(_reset_artwork_button)
|
||||
access_row.add_child(_reset_artwork_button)
|
||||
|
||||
|
||||
func _build_host_toggle(label_text: String, row: HBoxContainer) -> Button:
|
||||
|
|
@ -389,7 +393,10 @@ func _refresh_host_settings() -> void:
|
|||
return
|
||||
var host_visible: bool = _service.is_local_host() and _current_tab == 0
|
||||
_host_settings_panel.visible = host_visible
|
||||
if not host_visible or _discovery == null:
|
||||
if not host_visible:
|
||||
return
|
||||
_update_session_artwork_reset_button(_reset_artwork_button)
|
||||
if _discovery == null:
|
||||
return
|
||||
if not _room_name_edit.has_focus():
|
||||
_room_name_edit.text = _discovery.get_room_name()
|
||||
|
|
@ -481,7 +488,9 @@ func _on_host_status_changed(message: String, is_error: bool) -> void:
|
|||
"Open the game before enabling discovery.",
|
||||
]
|
||||
_host_discovery_status.text = "" if tooltip_only else message
|
||||
_host_discovery_status.visible = not _host_discovery_status.text.is_empty()
|
||||
# Keep this expanding label in the row even when there is no status text so
|
||||
# the session reset action remains pinned to the far-right edge.
|
||||
_host_discovery_status.visible = true
|
||||
_host_discovery_status.add_theme_color_override(
|
||||
"font_color",
|
||||
UtilityPageStyle.OCEAN_DANGER
|
||||
|
|
@ -491,7 +500,7 @@ func _on_host_status_changed(message: String, is_error: bool) -> void:
|
|||
|
||||
|
||||
func _build_active_rows() -> void:
|
||||
if _service.is_local_moderator():
|
||||
if _service.is_local_moderator() and not _service.is_local_host():
|
||||
_build_session_artwork_controls()
|
||||
var entries := _service.get_entries()
|
||||
if entries.is_empty():
|
||||
|
|
@ -502,35 +511,29 @@ func _build_active_rows() -> void:
|
|||
|
||||
|
||||
func _build_active_player_row(entry: PlayerListEntry) -> void:
|
||||
var row := _make_active_player_row()
|
||||
var identity_row := HBoxContainer.new()
|
||||
identity_row.add_theme_constant_override("separation", 8)
|
||||
row.add_child(identity_row)
|
||||
|
||||
var row := _make_row()
|
||||
var identity := Label.new()
|
||||
identity.clip_text = true
|
||||
identity.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
identity.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
var markers: Array[String] = []
|
||||
if entry.is_host:
|
||||
markers.append("host")
|
||||
identity.text = entry.display_name
|
||||
var roles: Array[String] = ["host" if entry.is_host else "player"]
|
||||
if entry.is_operator:
|
||||
markers.append("operator")
|
||||
roles.append("operator")
|
||||
if entry.is_local_player:
|
||||
markers.append("You")
|
||||
identity.text = "%s%s · %s\n%s" % [
|
||||
entry.display_name,
|
||||
" [%s]" % ", ".join(markers) if not markers.is_empty() else "",
|
||||
entry.compact_fingerprint,
|
||||
roles.append("you")
|
||||
_configure_identity_tooltip(
|
||||
identity,
|
||||
"identity fingerprint:\n%s\nrole: %s\nidentity status: %s" % [
|
||||
NetworkIdentityCrypto.format_fingerprint(entry.full_fingerprint),
|
||||
", ".join(roles),
|
||||
entry.continuity_state,
|
||||
]
|
||||
identity.tooltip_text = "Full identity fingerprint:\n%s" % (
|
||||
entry.full_fingerprint
|
||||
],
|
||||
)
|
||||
identity.add_theme_color_override(
|
||||
"font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY
|
||||
)
|
||||
identity_row.add_child(identity)
|
||||
row.add_child(identity)
|
||||
var ping := Label.new()
|
||||
ping.custom_minimum_size.x = 72
|
||||
ping.text = (
|
||||
|
|
@ -542,7 +545,7 @@ func _build_active_player_row(entry: PlayerListEntry) -> void:
|
|||
"font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY
|
||||
)
|
||||
ping.horizontal_alignment = HORIZONTAL_ALIGNMENT_RIGHT
|
||||
identity_row.add_child(ping)
|
||||
row.add_child(ping)
|
||||
|
||||
var actions := HBoxContainer.new()
|
||||
actions.alignment = BoxContainer.ALIGNMENT_END
|
||||
|
|
@ -634,31 +637,38 @@ func _configure_moderation_button(
|
|||
|
||||
|
||||
func _build_session_artwork_controls() -> void:
|
||||
var counts: Vector2i = _service.get_session_artwork_counts()
|
||||
var row := _make_row()
|
||||
var label := Label.new()
|
||||
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
label.clip_text = true
|
||||
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
label.text = "session artwork · %d layers · %d painted pixels" % [
|
||||
counts.x, counts.y,
|
||||
]
|
||||
label.add_theme_color_override(
|
||||
"font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY
|
||||
)
|
||||
row.add_child(label)
|
||||
var spacer := Control.new()
|
||||
spacer.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
spacer.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
row.add_child(spacer)
|
||||
var reset := Button.new()
|
||||
reset.text = "reset paint"
|
||||
reset.disabled = counts.x == 0
|
||||
reset.tooltip_text = "Clears all shared artwork from this session."
|
||||
reset.pressed.connect(func() -> void:
|
||||
_configure_session_artwork_reset_button(reset)
|
||||
_update_session_artwork_reset_button(reset)
|
||||
row.add_child(reset)
|
||||
|
||||
|
||||
func _configure_session_artwork_reset_button(button: Button) -> void:
|
||||
button.text = "reset paint"
|
||||
button.tooltip_text = "Clears all shared artwork from this session."
|
||||
button.pressed.connect(func() -> void:
|
||||
_confirm(
|
||||
"Clear all shared paint from this session?\nThis cannot be undone.",
|
||||
_service.reset_session_artwork,
|
||||
)
|
||||
)
|
||||
UtilityPageStyle.apply_compact_ocean_button(reset)
|
||||
row.add_child(reset)
|
||||
UtilityPageStyle.apply_compact_ocean_button(button)
|
||||
|
||||
|
||||
func _update_session_artwork_reset_button(button: Button) -> void:
|
||||
if button == null or _service == null:
|
||||
return
|
||||
button.disabled = _service.get_session_artwork_counts().x == 0
|
||||
|
||||
|
||||
func _configure_identity_tooltip(label: Label, text: String) -> void:
|
||||
label.mouse_filter = Control.MOUSE_FILTER_STOP
|
||||
label.tooltip_text = text
|
||||
|
||||
|
||||
func _build_relationship_rows() -> void:
|
||||
|
|
@ -673,12 +683,16 @@ func _build_relationship_rows() -> void:
|
|||
label.clip_text = true
|
||||
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
var fingerprint := str(record["fingerprint"])
|
||||
label.text = "%s · %s %s" % [
|
||||
label.text = "%s %s" % [
|
||||
str(record.get("last_known_display_name", "Player")),
|
||||
NetworkIdentityCrypto.compact_suffix(fingerprint),
|
||||
"Blocked" if bool(record.get("blocked", false)) else "Muted",
|
||||
]
|
||||
label.tooltip_text = NetworkIdentityCrypto.format_fingerprint(fingerprint)
|
||||
_configure_identity_tooltip(
|
||||
label,
|
||||
"identity fingerprint:\n%s" % (
|
||||
NetworkIdentityCrypto.format_fingerprint(fingerprint)
|
||||
),
|
||||
)
|
||||
label.add_theme_color_override(
|
||||
"font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY
|
||||
)
|
||||
|
|
@ -724,16 +738,20 @@ func _build_friend_rows() -> void:
|
|||
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
label.clip_text = true
|
||||
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
label.text = "%s · %s %s" % [
|
||||
label.text = "%s %s" % [
|
||||
display_name,
|
||||
NetworkIdentityCrypto.compact_suffix(fingerprint),
|
||||
"playing in %s" % str(room.get("room_name", "a public room"))
|
||||
if not room.is_empty()
|
||||
else "Online"
|
||||
if bool(friend.get("online", false))
|
||||
else "Offline",
|
||||
]
|
||||
label.tooltip_text = NetworkIdentityCrypto.format_fingerprint(fingerprint)
|
||||
_configure_identity_tooltip(
|
||||
label,
|
||||
"identity fingerprint:\n%s" % (
|
||||
NetworkIdentityCrypto.format_fingerprint(fingerprint)
|
||||
),
|
||||
)
|
||||
label.add_theme_color_override(
|
||||
"font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY
|
||||
)
|
||||
|
|
@ -811,12 +829,16 @@ func _build_ban_rows() -> void:
|
|||
label.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||
label.clip_text = true
|
||||
label.text_overrun_behavior = TextServer.OVERRUN_TRIM_ELLIPSIS
|
||||
label.text = "%s · %s banned %s" % [
|
||||
label.text = "%s banned %s" % [
|
||||
str(record.get("last_known_display_name", "Player")),
|
||||
NetworkIdentityCrypto.compact_suffix(fingerprint),
|
||||
Time.get_date_string_from_unix_time(int(record.get("banned_unix", 0))),
|
||||
]
|
||||
label.tooltip_text = NetworkIdentityCrypto.format_fingerprint(fingerprint)
|
||||
_configure_identity_tooltip(
|
||||
label,
|
||||
"identity fingerprint:\n%s" % (
|
||||
NetworkIdentityCrypto.format_fingerprint(fingerprint)
|
||||
),
|
||||
)
|
||||
label.add_theme_color_override(
|
||||
"font_color", UtilityPageStyle.OCEAN_TEXT_PRIMARY
|
||||
)
|
||||
|
|
@ -841,20 +863,6 @@ func _make_row() -> HBoxContainer:
|
|||
return row
|
||||
|
||||
|
||||
func _make_active_player_row() -> VBoxContainer:
|
||||
var panel := PanelContainer.new()
|
||||
panel.clip_contents = true
|
||||
panel.add_theme_stylebox_override(
|
||||
"panel", UtilityPageStyle.row_style(false)
|
||||
)
|
||||
_list.add_child(panel)
|
||||
var row := VBoxContainer.new()
|
||||
row.custom_minimum_size.y = 88
|
||||
row.add_theme_constant_override("separation", 4)
|
||||
panel.add_child(row)
|
||||
return row
|
||||
|
||||
|
||||
func _add_empty(text: String) -> void:
|
||||
var label := Label.new()
|
||||
label.text = text
|
||||
|
|
|
|||
|
|
@ -124,6 +124,12 @@ func _configure_style() -> void:
|
|||
if node is OrganizerTab:
|
||||
continue
|
||||
UtilityPageStyle.apply_ocean_button(node as BaseButton)
|
||||
_play_button.add_theme_stylebox_override(
|
||||
"normal",
|
||||
UtilityPageStyle.ocean_button_style(
|
||||
UtilityPageStyle.GREEN,
|
||||
),
|
||||
)
|
||||
_delete_button.add_theme_stylebox_override(
|
||||
"normal",
|
||||
UtilityPageStyle.ocean_button_style(
|
||||
|
|
@ -639,11 +645,11 @@ func _configure_controller_focus() -> void:
|
|||
_set_neighbors(button, button, _slot_name_edit, top, bottom)
|
||||
_set_neighbors(_import_button, _import_button, _slot_name_edit, last, _back_button)
|
||||
_set_neighbors(_slot_name_edit, first, _slot_name_edit, _saves_tab, _play_button)
|
||||
_set_neighbors(_play_button, _import_button, _rename_button, _slot_name_edit, _duplicate_button)
|
||||
_set_neighbors(_rename_button, _play_button, _rename_button, _slot_name_edit, _export_button)
|
||||
_set_neighbors(_duplicate_button, _import_button, _export_button, _play_button, _delete_button)
|
||||
_set_neighbors(_export_button, _duplicate_button, _export_button, _rename_button, _delete_button)
|
||||
_set_neighbors(_delete_button, _import_button, _delete_button, _duplicate_button, _back_button)
|
||||
_set_neighbors(_play_button, _import_button, _play_button, _slot_name_edit, _rename_button)
|
||||
_set_neighbors(_rename_button, _import_button, _duplicate_button, _play_button, _export_button)
|
||||
_set_neighbors(_duplicate_button, _rename_button, _duplicate_button, _play_button, _delete_button)
|
||||
_set_neighbors(_export_button, _import_button, _delete_button, _rename_button, _back_button)
|
||||
_set_neighbors(_delete_button, _export_button, _delete_button, _duplicate_button, _back_button)
|
||||
_set_neighbors(_back_button, _back_button, _back_button, _import_button, _back_button)
|
||||
return
|
||||
_saves_tab.focus_neighbor_bottom = _saves_tab.get_path_to(_new_slot_name)
|
||||
|
|
|
|||
|
|
@ -165,11 +165,10 @@ scroll_active = false
|
|||
autowrap_mode = 2
|
||||
vertical_alignment = 1
|
||||
|
||||
[node name="Actions" type="GridContainer" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/PageStack/SavesPage/DetailsColumn"]
|
||||
[node name="Actions" type="VBoxContainer" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/PageStack/SavesPage/DetailsColumn"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_constants/h_separation = 10
|
||||
theme_override_constants/v_separation = 10
|
||||
columns = 2
|
||||
theme_override_constants/separation = 10
|
||||
|
||||
[node name="PlaySlotButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/PageStack/SavesPage/DetailsColumn/Actions"]
|
||||
unique_name_in_owner = true
|
||||
|
|
@ -179,7 +178,14 @@ size_flags_horizontal = 3
|
|||
focus_mode = 2
|
||||
text = "play"
|
||||
|
||||
[node name="RenameSlotButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/PageStack/SavesPage/DetailsColumn/Actions"]
|
||||
[node name="SecondaryActions" type="GridContainer" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/PageStack/SavesPage/DetailsColumn/Actions"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
theme_override_constants/h_separation = 10
|
||||
theme_override_constants/v_separation = 10
|
||||
columns = 2
|
||||
|
||||
[node name="RenameSlotButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/PageStack/SavesPage/DetailsColumn/Actions/SecondaryActions"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 46)
|
||||
layout_mode = 2
|
||||
|
|
@ -187,7 +193,7 @@ size_flags_horizontal = 3
|
|||
focus_mode = 2
|
||||
text = "rename"
|
||||
|
||||
[node name="DuplicateSlotButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/PageStack/SavesPage/DetailsColumn/Actions"]
|
||||
[node name="DuplicateSlotButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/PageStack/SavesPage/DetailsColumn/Actions/SecondaryActions"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 46)
|
||||
layout_mode = 2
|
||||
|
|
@ -195,7 +201,7 @@ size_flags_horizontal = 3
|
|||
focus_mode = 2
|
||||
text = "duplicate"
|
||||
|
||||
[node name="ExportSlotButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/PageStack/SavesPage/DetailsColumn/Actions"]
|
||||
[node name="ExportSlotButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/PageStack/SavesPage/DetailsColumn/Actions/SecondaryActions"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 46)
|
||||
layout_mode = 2
|
||||
|
|
@ -203,7 +209,7 @@ size_flags_horizontal = 3
|
|||
focus_mode = 2
|
||||
text = "export"
|
||||
|
||||
[node name="DeleteSlotButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/PageStack/SavesPage/DetailsColumn/Actions"]
|
||||
[node name="DeleteSlotButton" type="Button" parent="MainPanel/OuterMargin/Layout/ContentPanel/ContentMargin/PageStack/SavesPage/DetailsColumn/Actions/SecondaryActions"]
|
||||
unique_name_in_owner = true
|
||||
custom_minimum_size = Vector2(0, 46)
|
||||
layout_mode = 2
|
||||
|
|
|
|||
|
|
@ -464,6 +464,7 @@ func _update_title_layout() -> void:
|
|||
Vector2(field_width, field_height),
|
||||
compact_layout
|
||||
)
|
||||
_configure_title_controller_navigation()
|
||||
if not _title_settings_transition_active:
|
||||
call_deferred("_capture_title_bubble_rest_position")
|
||||
if _awaiting_start_input and not _title_entry_transition_active:
|
||||
|
|
@ -1483,9 +1484,28 @@ func _set_title_bubbles_interactive(interactive: bool) -> void:
|
|||
else Control.MOUSE_FILTER_IGNORE
|
||||
)
|
||||
if interactive:
|
||||
ControllerFocusNavigationType.configure_spatial_neighbors(
|
||||
_get_title_buttons()
|
||||
)
|
||||
_configure_title_controller_navigation()
|
||||
|
||||
|
||||
func _configure_title_controller_navigation() -> void:
|
||||
ControllerFocusNavigationType.configure_spatial_neighbors(
|
||||
_get_title_buttons()
|
||||
)
|
||||
# The large Play bubble overlaps the inward edge of every surrounding
|
||||
# bubble. Center-only spatial scoring otherwise links the four outside
|
||||
# bubbles into a ring with no route back into Play.
|
||||
_join_game_button.focus_neighbor_right = (
|
||||
_join_game_button.get_path_to(_play_button)
|
||||
)
|
||||
_settings_button.focus_neighbor_left = (
|
||||
_settings_button.get_path_to(_play_button)
|
||||
)
|
||||
_credits_button.focus_neighbor_right = (
|
||||
_credits_button.get_path_to(_play_button)
|
||||
)
|
||||
_quit_button.focus_neighbor_left = (
|
||||
_quit_button.get_path_to(_play_button)
|
||||
)
|
||||
|
||||
|
||||
func _capture_title_bubble_rest_position() -> void:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue