Skip to content

Commit 891d131

Browse files
committed
Fix portrait controller visibility and add per-button action toggles with undo
- Portrait gameplay: switch from VStack split to ZStack overlay so the game fills the screen and the virtual controller is bottom-aligned without an opaque dark background panel for built-in skins. - VirtualControllerView: remove Color(white: 0.10) fallback in portraitLayout and replace single action-pad visibility with independent per-button checks for triangle, cross, square, and circle. - PadLayoutStore: add per-button visibility fallback (explicit key wins, then group fallback). Save/load per-button visibility for action buttons in a separate INI namespace with -1 sentinel for absent keys. - PadLayoutEditView: add ephemeral undo stack for in-progress edits. Cancel restores the original snapshot. Save clears undo state and persists. - Editor visibility row: split the combined ACTION toggle into four separate toggles with user-facing labels: Toggle: X, Toggle: O, Toggle: Triangle, Toggle: Square.
1 parent c239987 commit 891d131

4 files changed

Lines changed: 193 additions & 48 deletions

File tree

app/src/main/swift/Models/PadLayoutStore.swift

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,9 @@ enum VirtualPadButtonOffset {
4747
final class PadLayoutStore: @unchecked Sendable {
4848
static let shared = PadLayoutStore()
4949

50-
static let groupIDs = ["dpad", "action", "l1", "l2", "r1", "r2", "lstick", "rstick", "select", "start"]
50+
static let actionButtonIDs = ["cross", "circle", "square", "triangle"]
5151
static let perButtonIDs = ["triangle", "circle", "square", "cross", "up", "down", "left", "right"]
52+
static let groupIDs = ["dpad", "action", "l1", "l2", "r1", "r2", "lstick", "rstick", "select", "start"]
5253

5354
var portrait: [String: PadGroupPosition] = [:]
5455
var landscape: [String: PadGroupPosition] = [:]
@@ -140,10 +141,15 @@ final class PadLayoutStore: @unchecked Sendable {
140141

141142
// MARK: - Visibility helpers
142143

143-
/// Returns whether a control group is visible. Default is `true` (visible).
144-
/// Visibility is stored globally (applies to both portrait and landscape).
144+
/// Returns whether a control or button is visible.
145+
/// If an explicit per-button key exists, it wins.
146+
/// Otherwise, fall back to the group visibility (default: visible).
145147
func isControlVisible(_ id: String) -> Bool {
146-
controlVisibility[id] ?? true
148+
if let explicit = controlVisibility[id] {
149+
return explicit
150+
}
151+
let group = groupID(for: id)
152+
return controlVisibility[group] ?? true
147153
}
148154

149155
func setControlVisible(_ id: String, visible: Bool) {
@@ -192,6 +198,14 @@ final class PadLayoutStore: @unchecked Sendable {
192198
let isVisible = isControlVisible(id)
193199
ARMSX2Bridge.setINIFloat("ARMSX2iOS/PadLayout/ControlVisibility", key: id, value: isVisible ? 1.0 : 0.0)
194200
}
201+
// Per-button visibility overrides (only action buttons in this pass).
202+
for id in Self.actionButtonIDs {
203+
if let explicit = controlVisibility[id] {
204+
ARMSX2Bridge.setINIFloat("ARMSX2iOS/PadLayout/ControlVisibility", key: id, value: explicit ? 1.0 : 0.0)
205+
} else {
206+
ARMSX2Bridge.setINIFloat("ARMSX2iOS/PadLayout/ControlVisibility", key: id, value: -1.0)
207+
}
208+
}
195209
}
196210

197211
func load() {
@@ -266,6 +280,13 @@ final class PadLayoutStore: @unchecked Sendable {
266280
controlVisibility[id] = (value > 0.5)
267281
}
268282
}
283+
// Per-button visibility overrides.
284+
for id in Self.actionButtonIDs {
285+
let value = ARMSX2Bridge.getINIFloat("ARMSX2iOS/PadLayout/ControlVisibility", key: id, defaultValue: -1)
286+
if value >= 0 {
287+
controlVisibility[id] = (value > 0.5)
288+
}
289+
}
269290
}
270291

271292
func resetPortrait() {

app/src/main/swift/Views/GameScreenView.swift

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -86,21 +86,15 @@ struct GameScreenView: View {
8686
}
8787
.ignoresSafeArea()
8888
} else {
89-
// Portrait: split layout. Full-phone skins are skipped until viewport metadata is parsed.
89+
// Portrait: full-screen game with controller overlay at bottom.
9090
ZStack {
91-
VStack(spacing: 0) {
92-
MetalGameView()
91+
MetalGameView()
92+
if effectiveVirtualPadVisible {
93+
VirtualControllerView()
9394
.frame(height: geo.size.height / 2)
94-
if effectiveVirtualPadVisible {
95-
VirtualControllerView()
96-
.frame(height: geo.size.height / 2)
97-
} else {
98-
Spacer()
99-
}
100-
}
101-
.overlay(alignment: .topTrailing) {
102-
menuButtonOverlay(isLandscape: false)
95+
.frame(maxHeight: .infinity, alignment: .bottom)
10396
}
97+
menuButtonOverlay(isLandscape: false)
10498
}
10599
}
106100
}

0 commit comments

Comments
 (0)