summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/LivingVillage.Desktop.Tests/PrototypeTests.fs23
-rw-r--r--src/LivingVillage.Desktop/Game.fs6
-rw-r--r--src/LivingVillage.Desktop/TitleScreen.fs21
3 files changed, 47 insertions, 3 deletions
diff --git a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
index c85287f..77d2530 100644
--- a/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
+++ b/src/LivingVillage.Desktop.Tests/PrototypeTests.fs
@@ -219,6 +219,29 @@ type PrototypeTests () =
Assert.AreEqual<SimulationControl>(original, restoredControl)
[<TestMethod>]
+ member _.ControlsPageKeyColumnIsUniformAndNeverOverprintsActions () =
+ let layout = TitleScreen.layout 1280 720
+ let rows = MenuState.controlRows
+
+ // Every row: key-name rectangle and action rectangle must be disjoint.
+ for rowIndex in 0 .. rows.Length - 1 do
+ let keyRect, actionRect = TitleScreen.controlRowRects layout rowIndex
+ Assert.IsTrue(TitleScreen.rectsDisjoint keyRect actionRect, sprintf "row %d key overlaps action" rowIndex)
+
+ // The key column is uniform: every row shares the same x and width.
+ let keyRects = [ 0 .. rows.Length - 1 ] |> List.map (fun index -> TitleScreen.controlRowRects layout index |> fst)
+ Assert.AreEqual<int>(1, keyRects |> List.map (fun rect -> rect.X) |> List.distinct |> List.length)
+ Assert.AreEqual<int>(1, keyRects |> List.map (fun rect -> rect.Width) |> List.distinct |> List.length)
+
+ // The key column fits the longest key name (latin glyph: 6px * scale 2 per char).
+ let longestKey = rows |> List.maxBy (fun row -> row.Keys.Length)
+ Assert.IsTrue(layout.ControlsKeyWidth >= longestKey.Keys.Length * 12, sprintf "key width %d too small for %s" layout.ControlsKeyWidth longestKey.Keys)
+
+ // The controls panel holds every row plus the footer inside the viewport.
+ let panelBottom = layout.ControlsPanelY + 60 + rows.Length * layout.ControlsRowHeight + 48
+ Assert.IsTrue(panelBottom <= 720, sprintf "controls panel bottom %d overflows" panelBottom)
+
+ [<TestMethod>]
member _.CornerStatusShowsDayMoneyAndEnergyFromRealData () =
let world = Sim.initialWorldN 42UL 0
let lines = M5Interaction.cornerStatusLines world
diff --git a/src/LivingVillage.Desktop/Game.fs b/src/LivingVillage.Desktop/Game.fs
index 7337129..25b8c48 100644
--- a/src/LivingVillage.Desktop/Game.fs
+++ b/src/LivingVillage.Desktop/Game.fs
@@ -547,6 +547,8 @@ type LivingVillageGame() as this =
| LoadError -> 4
| Playing -> 0
let rowHeight = if menu.Page = Controls then 25 else layout.MenuLineHeight
+ let panelY =
+ if menu.Page = Controls then layout.ControlsPanelY else layout.PanelY
let panelHeight =
min (height - panelY - 16)
(max layout.PanelHeight (120 + contentRows * rowHeight + 48))
@@ -622,7 +624,9 @@ type LivingVillageGame() as this =
drawText innerX (layout.HeadingY) mutedColor (ChineseText.menuHeading ControlsHeading)
MenuState.controlRows
|> List.iteri (fun index row ->
- drawText innerX (contentY + index * 25) textColor (sprintf "%s %s" row.Keys row.Action))
+ let keyRect, actionRect = TitleScreen.controlRowRects layout index
+ PixelText.draw spriteBatch pixel cjkAtlas keyRect.X keyRect.Y textScale textColor row.Keys
+ PixelText.draw spriteBatch pixel cjkAtlas actionRect.X actionRect.Y textScale textColor row.Action)
drawText innerX (panelY + panelHeight - 40) mutedColor "ESC 返回"
| LoadError ->
drawText innerX (layout.HeadingY) (Color(240, 130, 120)) "读取失败"
diff --git a/src/LivingVillage.Desktop/TitleScreen.fs b/src/LivingVillage.Desktop/TitleScreen.fs
index 00cf5b8..7ec46d2 100644
--- a/src/LivingVillage.Desktop/TitleScreen.fs
+++ b/src/LivingVillage.Desktop/TitleScreen.fs
@@ -29,7 +29,12 @@ module TitleScreen =
FirstItemY: int
CursorX: int
CursorWidth: int
- TextX: int }
+ TextX: int
+ ControlsPanelY: int
+ ControlsRowHeight: int
+ ControlsKeyX: int
+ ControlsKeyWidth: int
+ ControlsActionX: int }
type TextRect =
{ X: int
@@ -43,6 +48,13 @@ module TitleScreen =
|| a.Y + a.Height <= b.Y
|| b.Y + b.Height <= a.Y
+ /// Key-name and action rectangles for one controls-page row. The key column has a
+ /// fixed width shared by every row, so all action texts start on the same x.
+ let controlRowRects (layout: TitleLayout) (rowIndex: int) : TextRect * TextRect =
+ let y = layout.FirstItemY + rowIndex * layout.ControlsRowHeight
+ { X = layout.ControlsKeyX; Y = y; Width = layout.ControlsKeyWidth; Height = 16 },
+ { X = layout.ControlsActionX; Y = y; Width = layout.PanelX + layout.PanelWidth - layout.ControlsActionX - 24; Height = 16 }
+
/// Cursor and label rectangles for one menu row. The cursor is drawn in its own
/// column; the label always starts to the right of it, so they can never overprint.
let selectionRects (layout: TitleLayout) (rowIndex: int) : TextRect * TextRect =
@@ -111,4 +123,9 @@ module TitleScreen =
FirstItemY = height / 2 - 64 + 60
CursorX = (width - panelWidth) / 2 + 24
CursorWidth = 20
- TextX = (width - panelWidth) / 2 + 24 + 20 + 12 }
+ TextX = (width - panelWidth) / 2 + 24 + 20 + 12
+ ControlsPanelY = max 16 (height - 480)
+ ControlsRowHeight = 25
+ ControlsKeyX = (width - panelWidth) / 2 + 24
+ ControlsKeyWidth = 120
+ ControlsActionX = (width - panelWidth) / 2 + 24 + 120 + 12 }