Skip to content

Commit 2af0564

Browse files
committed
Fix iOS layout editor orientation handling
- Remove forced UIWindowScene.requestGeometryUpdate calls from the editor. The picker now selects which layout data is being edited, not which orientation the device should rotate to. - Make the editor canvas adaptive to actual device geometry: landscape canvas when width > height, portrait canvas otherwise. - Initialize editLandscape from UIScreen.main.bounds on appear so the editor opens in the correct mode when the device is already landscape. - Add a banner when the picker data mode differs from actual geometry, prompting the user to rotate for the full canvas. - Fix editorPortraitContent to use editLandscape instead of hardcoded false, so landscape layout data is editable even in the portrait canvas. - Remove the rotation-prompt overlay inside landscapeEditor; the body now handles geometry selection and the banner handles user guidance.
1 parent 6189d47 commit 2af0564

1 file changed

Lines changed: 80 additions & 111 deletions

File tree

app/src/main/swift/Views/Settings/PadLayoutEditView.swift

Lines changed: 80 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,42 @@ struct PadLayoutEditView: View {
2121
@State private var originalSnapshot: PadLayoutSnapshot? = nil
2222

2323
var body: some View {
24-
Group {
25-
if editLandscape {
26-
landscapeEditor
27-
} else {
28-
portraitEditor
24+
GeometryReader { geo in
25+
let isActuallyLandscape = geo.size.width > geo.size.height
26+
ZStack {
27+
if isActuallyLandscape {
28+
landscapeEditorCanvas(width: geo.size.width, height: geo.size.height)
29+
} else {
30+
portraitEditorCanvas(width: geo.size.width, height: geo.size.height)
31+
}
32+
33+
// Banner when editing data for a different orientation than current geometry
34+
if isActuallyLandscape != editLandscape {
35+
VStack {
36+
HStack(spacing: 6) {
37+
Image(systemName: "exclamationmark.triangle.fill")
38+
Text(editLandscape
39+
? "Editing landscape layout in portrait. Rotate device to landscape for full canvas."
40+
: "Editing portrait layout in landscape. Rotate device to portrait for full canvas.")
41+
.font(.caption.weight(.semibold))
42+
.multilineTextAlignment(.center)
43+
.lineLimit(3)
44+
}
45+
.foregroundStyle(.yellow)
46+
.padding(.horizontal, 12)
47+
.padding(.vertical, 8)
48+
.background(.black.opacity(0.6), in: RoundedRectangle(cornerRadius: 8))
49+
Spacer()
50+
}
51+
.padding(.top, 50)
52+
.padding(.horizontal, 20)
53+
.allowsHitTesting(false)
54+
}
55+
56+
VStack {
57+
compactToolbar
58+
Spacer()
59+
}
2960
}
3061
}
3162
.navigationBarHidden(true)
@@ -35,108 +66,61 @@ struct PadLayoutEditView: View {
3566
if originalSnapshot == nil {
3667
originalSnapshot = captureSnapshot()
3768
}
69+
let bounds = UIScreen.main.bounds
70+
editLandscape = bounds.width > bounds.height
3871
}
3972
.onDisappear {
4073
NSLog("@@PAD_LAYOUT@@ disappear")
4174
NotificationCenter.default.post(name: Notification.Name("ARMSX2iOSPadLayoutEditorDismissed"), object: nil)
4275
}
43-
.onChange(of: editLandscape) { _, isLandscape in
44-
if isLandscape {
45-
requestOrientation(.landscape)
46-
} else {
47-
requestOrientation(.portrait)
48-
}
49-
}
5076
}
5177

52-
// MARK: - Landscape editor (full-screen overlay, prompts rotation if needed)
53-
private var landscapeEditor: some View {
54-
GeometryReader { geo in
55-
let isActuallyLandscape = geo.size.width > geo.size.height
56-
if isActuallyLandscape {
57-
ZStack {
58-
Color.black.opacity(0.4)
59-
.allowsHitTesting(false)
60-
61-
// Subtle safe zone guide
62-
let margin: CGFloat = 0.05
63-
let guideRect = CGRect(
64-
x: geo.size.width * margin,
65-
y: geo.size.height * margin,
66-
width: geo.size.width * (1 - margin * 2),
67-
height: geo.size.height * (1 - margin * 2)
68-
)
69-
Rectangle()
70-
.stroke(style: StrokeStyle(lineWidth: 1, dash: [6, 4]))
71-
.foregroundStyle(.yellow.opacity(0.3))
72-
.frame(width: guideRect.width, height: guideRect.height)
73-
.position(x: guideRect.midX, y: guideRect.midY)
74-
75-
customSkinPreview(isLandscape: true, width: geo.size.width, height: geo.size.height)
76-
77-
editorContent(areaW: geo.size.width, areaH: geo.size.height)
78-
79-
VStack {
80-
compactToolbar
81-
Spacer()
82-
}
83-
}
84-
.contentShape(Rectangle())
85-
.ignoresSafeArea()
86-
} else {
87-
// Device is still portrait — show rotation prompt
88-
ZStack {
89-
Color.black
90-
91-
VStack(spacing: 16) {
92-
Image(systemName: "rotate.right")
93-
.font(.system(size: 48))
94-
.foregroundStyle(.secondary)
95-
Text("Rotate your device to landscape to edit the landscape layout.")
96-
.font(.headline)
97-
.foregroundStyle(.secondary)
98-
.multilineTextAlignment(.center)
99-
.padding(.horizontal, 32)
100-
}
78+
// MARK: - Landscape editor canvas (always shown when device is landscape)
79+
private func landscapeEditorCanvas(width: CGFloat, height: CGFloat) -> some View {
80+
ZStack {
81+
Color.black.opacity(0.4)
82+
.allowsHitTesting(false)
10183

102-
VStack {
103-
compactToolbar
104-
Spacer()
105-
}
106-
}
107-
.ignoresSafeArea()
108-
}
84+
// Subtle safe zone guide
85+
let margin: CGFloat = 0.05
86+
let guideRect = CGRect(
87+
x: width * margin,
88+
y: height * margin,
89+
width: width * (1 - margin * 2),
90+
height: height * (1 - margin * 2)
91+
)
92+
Rectangle()
93+
.stroke(style: StrokeStyle(lineWidth: 1, dash: [6, 4]))
94+
.foregroundStyle(.yellow.opacity(0.3))
95+
.frame(width: guideRect.width, height: guideRect.height)
96+
.position(x: guideRect.midX, y: guideRect.midY)
97+
98+
customSkinPreview(isLandscape: true, width: width, height: height)
99+
100+
editorContent(areaW: width, areaH: height)
109101
}
102+
.contentShape(Rectangle())
103+
.ignoresSafeArea()
110104
}
111105

112-
// MARK: - Portrait editor (matches Phase 1 gameplay: top game area, bottom controller deck)
113-
private var portraitEditor: some View {
114-
GeometryReader { geo in
115-
let gameHeight = min(geo.size.width * 3 / 4, geo.size.height * 0.55)
116-
let padHeight = geo.size.height - gameHeight
117-
ZStack {
118-
VStack(spacing: 0) {
119-
// Top: game viewport area
120-
Color.black
121-
.allowsHitTesting(false)
122-
.frame(height: gameHeight)
123-
124-
// Bottom: controller deck editing area
125-
ZStack {
126-
Color.black
127-
customSkinPreview(isLandscape: false, width: geo.size.width, height: padHeight)
128-
editorPortraitContent(areaW: geo.size.width, areaH: padHeight)
129-
}
130-
.frame(height: padHeight)
131-
.clipped()
132-
}
106+
// MARK: - Portrait editor canvas (always shown when device is portrait)
107+
private func portraitEditorCanvas(width: CGFloat, height: CGFloat) -> some View {
108+
let gameHeight = min(width * 3 / 4, height * 0.55)
109+
let padHeight = height - gameHeight
110+
VStack(spacing: 0) {
111+
// Top: game viewport area
112+
Color.black
113+
.allowsHitTesting(false)
114+
.frame(height: gameHeight)
133115

134-
VStack {
135-
compactToolbar
136-
Spacer()
137-
}
116+
// Bottom: controller deck editing area
117+
ZStack {
118+
Color.black
119+
customSkinPreview(isLandscape: false, width: width, height: padHeight)
120+
editorPortraitContent(areaW: width, areaH: padHeight)
138121
}
139-
.contentShape(Rectangle())
122+
.frame(height: padHeight)
123+
.clipped()
140124
}
141125
}
142126

@@ -307,11 +291,11 @@ struct PadLayoutEditView: View {
307291
ZStack {
308292
// Individual buttons for action and dpad
309293
ForEach(PadLayoutStore.perButtonIDs, id: \.self) { (id: String) in
310-
DraggableButton(id: id, areaW: areaW, areaH: areaH, isLandscape: false, onBeginEdit: pushSnapshot)
294+
DraggableButton(id: id, areaW: areaW, areaH: areaH, isLandscape: editLandscape, onBeginEdit: pushSnapshot)
311295
}
312296
// Group widgets for other controls
313297
ForEach(PadLayoutStore.groupIDs.filter { $0 != "action" && $0 != "dpad" }, id: \.self) { (id: String) in
314-
DraggableGroup(id: id, areaW: areaW, areaH: areaH, isLandscape: false, onBeginEdit: pushSnapshot)
298+
DraggableGroup(id: id, areaW: areaW, areaH: areaH, isLandscape: editLandscape, onBeginEdit: pushSnapshot)
315299
}
316300
}
317301
.environment(\.padSkin, settings.virtualPadSkin)
@@ -344,21 +328,6 @@ struct PadLayoutEditView: View {
344328
}
345329
}
346330

347-
// MARK: - Orientation helper
348-
@MainActor
349-
private func requestOrientation(_ mask: UIInterfaceOrientationMask) {
350-
if #available(iOS 16.0, *) {
351-
for case let scene as UIWindowScene in UIApplication.shared.connectedScenes {
352-
guard scene.activationState == .foregroundActive || scene.activationState == .foregroundInactive else { continue }
353-
let preferences = UIWindowScene.GeometryPreferences.iOS(interfaceOrientations: mask)
354-
scene.requestGeometryUpdate(preferences) { error in
355-
NSLog("@@PAD_LAYOUT@@ orientation request failed: %@", error.localizedDescription)
356-
}
357-
break
358-
}
359-
}
360-
}
361-
362331
// MARK: - Draggable group widget
363332
private struct DraggableGroup: View {
364333
let id: String

0 commit comments

Comments
 (0)