Introduce a textured cube with raylib #2
No files matched your search
+10
@@ -0,0 +1,10 @@
|
||||
build
|
||||
.build
|
||||
.DS_Store
|
||||
.gradle
|
||||
gradle-daemon-jvm.properties
|
||||
.idea
|
||||
.kotlin
|
||||
local.properties
|
||||
Package.resolved
|
||||
*.xcodeproj
|
||||
@@ -0,0 +1,151 @@
|
||||
# OGS Mahjong (`mj`)
|
||||
|
||||
A macOS SwiftUI app for **OGS Mahjong**, built on the **Dialect v4** declarative
|
||||
reactive engine (see the sibling `../dialect` repo — this project is a
|
||||
downstream consumer of its code generator).
|
||||
|
||||
Business logic lives in `dialect.yml` (YAML). A generator turns it into
|
||||
`game/ver-mac/src/dialect.swift` (the runtime engine + `struct F` field
|
||||
constants + component/should/effect registration). The Swift app shell
|
||||
(`GameApp`/`AppDelegate`) and the SwiftUI views live outside the generated file
|
||||
and talk to the engine through generated functions like `rootSet(...)`.
|
||||
|
||||
Remote: `https://git.opengamestudio.org/mahjong/mahjong` · Version: `2.0.0`
|
||||
|
||||
> Current `dialect.yml` is still the "Root / counter" template inherited from the
|
||||
> dialect example — the Mahjong game logic has not been written yet. The app
|
||||
> shell, packaging and symlink wiring are in place.
|
||||
|
||||
## Structure
|
||||
|
||||
```
|
||||
dialect.yml Single source of truth for the reactive logic.
|
||||
Generates game/ver-mac/src/dialect.swift
|
||||
|
||||
components/ SHARED source-of-truth for all components.
|
||||
other/other.swift Shared helpers (otherPrintStderr, otherWriteFile,
|
||||
otherSetupConsoleLogging, ...)
|
||||
root/root.swift RootComponent (owns DialectController, registers
|
||||
effects + shoulds, sets didSetup on setup())
|
||||
root/rootVM.swift RootVM (ObservableObject / @Published countText)
|
||||
root/mac/view/RootView.swift Per-platform SwiftUI view
|
||||
|
||||
game/ver-mac/ macOS app (SPM executable target "ogs-mahjong")
|
||||
Package.swift SPM manifest (macOS 13+, swift-tools 5.9)
|
||||
Info.plist Bundle id org.opengamestudio.Mahjong, exec ogs-mahjong
|
||||
Resources/AppIcon.icns App icon
|
||||
src/
|
||||
GameApp.swift @main SwiftUI App entry
|
||||
AppDelegate.swift Creates RootComponent + RootVM, calls root.setup()
|
||||
dialect.swift GENERATED — do not hand-edit (see chicken-egg below)
|
||||
other/other.swift -> symlink components/other/other.swift
|
||||
root/root.swift -> symlink components/root/root.swift
|
||||
root/rootVM.swift -> symlink components/root/rootVM.swift
|
||||
root/view/RootView.swift-> symlink components/root/mac/view/RootView.swift
|
||||
|
||||
ref/swiftraylib Symlink to ../../swiftraylib — the raylib/SwiftUI
|
||||
renderer sample the game will use for 3D rendering.
|
||||
|
||||
util/ Build/run scripts (see Build & Run)
|
||||
paths APP_MAC = .build/OGS Mahjong.app
|
||||
build-mac generateDialect -> buildMac -> packageMac
|
||||
run-mac runMac
|
||||
rm-mac-geom Resets saved window geometry
|
||||
step/ Sourced steps: generateDialect, buildMac,
|
||||
packageMac, runMac
|
||||
|
||||
history/gitjs/ GitJS "Glavred" history + the original JS source of the
|
||||
web Mahjong, kept for reference — not part of the build.
|
||||
```
|
||||
|
||||
## Symlink parity rule
|
||||
|
||||
`components/` is the source of truth; `game/ver-mac/src/` contains real
|
||||
directories whose files are **relative symlinks** back into `components/`:
|
||||
|
||||
```
|
||||
src/other/other.swift -> ../../../../components/other/other.swift (4 up)
|
||||
src/root/root.swift -> ../../../../components/root/root.swift (4 up)
|
||||
src/root/rootVM.swift -> ../../../../components/root/rootVM.swift (4 up)
|
||||
src/root/view/RootView.swift-> ../../../../../components/root/mac/view/RootView.swift (5 up)
|
||||
```
|
||||
|
||||
SPM silently **skips** a dangling symlink (no error), so after moving
|
||||
`components/` or a subdir, audit for broken links:
|
||||
|
||||
```bash
|
||||
find . -type l ! -exec test -e {} \; -not -path './.build/*'
|
||||
```
|
||||
|
||||
## Prerequisite: the generator
|
||||
|
||||
`util/step/generateDialect` calls the **sibling** dialect repo:
|
||||
|
||||
```bash
|
||||
$SDIR/../../dialect/util/run-generator --file=$SDIR/../dialect.yml
|
||||
```
|
||||
|
||||
So `/Volumes/MOE/p/dialect` must exist and its generator must be built before
|
||||
`util/build-mac` can regenerate:
|
||||
|
||||
```bash
|
||||
(cd ../dialect && util/build-generator) # once, or after dialect core changes
|
||||
```
|
||||
|
||||
The generator resolves the `output:` path **relative to `dialect.yml`'s
|
||||
directory**, which is why `dialect.yml` says `game/ver-mac/src/dialect.swift`.
|
||||
|
||||
## Build & Run
|
||||
|
||||
```bash
|
||||
util/build-mac # generateDialect + swift build + package OGS Mahjong.app
|
||||
util/run-mac # runs .build/OGS Mahjong.app/Contents/MacOS/ogs-mahjong
|
||||
util/rm-mac-geom # reset the app's saved window geometry
|
||||
```
|
||||
|
||||
`build-mac` sources `util/paths` then the `util/step/*` scripts in order:
|
||||
|
||||
1. `generateDialect` — runs the sibling generator, writes `src/dialect.swift`
|
||||
2. `buildMac` — `cd game/ver-mac && swift build`
|
||||
3. `packageMac` — assembles `.build/OGS Mahjong.app` (binary `ogs-mahjong`,
|
||||
`Info.plist`, `Resources/AppIcon.icns`)
|
||||
|
||||
## Editing the dialect
|
||||
|
||||
- Add/modify contexts and components in `dialect.yml`, then run
|
||||
`util/build-mac` to regenerate `dialect.swift`.
|
||||
- **Chicken-egg**: `struct F` (field-name constants) lives only in the
|
||||
generated `dialect.swift`. Adding a new field to a context in `dialect.yml`
|
||||
requires hand-patching its `static let <field> = "<field>"` into `struct F`
|
||||
in `dialect.swift` (ASCII-alphabetical order) if you cannot regenerate first,
|
||||
otherwise the build fails on `F.<field>` being unresolved.
|
||||
- `dialect.swift` is generated — edits there are overwritten on the next
|
||||
`util/build-mac`. Change the source (`dialect.yml` / `components/`) instead.
|
||||
- **Validate `dialect.yml` after edits** (e.g. `ruby -ryaml -e 'YAML.load_file("dialect.yml")'`).
|
||||
Every should-branch label (`1. Upon each ...`) MUST end with a colon — the
|
||||
parser (`components/yml/ymlFun.swift`) blindly `dropLast()`s it, so a missing
|
||||
colon silently eats the label's last character in the generated `about`
|
||||
comment. Note the file can never pass a *strict* YAML parser: condition and
|
||||
reaction lines (under `if:` / `then:`) contain unquoted `: ` (e.g.
|
||||
`c.countText = "Count: '\(c.count)'"`), which standard YAML rejects. So treat
|
||||
a parser complaint pointing *inside* an `if:`/`then:` block as expected; any
|
||||
complaint elsewhere (like a missing branch-label colon) is a real bug.
|
||||
|
||||
## Swift 6
|
||||
|
||||
The project currently builds in Swift 5 language mode
|
||||
(`util/step/buildMac` has the Swift 6 flag commented out). Enabling
|
||||
`-Xswiftc -swift-version -Xswiftc 6` fails today on the singleton/`@MainActor`
|
||||
concurrency errors (`RootComponent.singleton`, `RootVM.shared` in
|
||||
`components/root/`, and the `RootVM.shared!.countText = ...` oneliner in the
|
||||
generated `dialect.swift`). Do not flip it on without addressing those.
|
||||
|
||||
## Conventions
|
||||
|
||||
- `game/ver-mac/.build/`, `build/`, `.DS_Store`, Xcode/Gradle artifacts are
|
||||
git-ignored.
|
||||
- Prefer editing files in `components/` (the real files); the `src/` entries are
|
||||
symlinks.
|
||||
- The generated UI callback plumbing is nonisolated; UI singletons are weak
|
||||
(`RootComponent.singleton`, `RootVM.shared`) and are accessed from the
|
||||
generated oneliners with force-unwrap (`RootVM.shared!.countText = ...`).
|
||||
@@ -0,0 +1,15 @@
|
||||
Copyright (c) 2026 OpenGameStudio
|
||||
|
||||
This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose, including commercial applications, and to alter it and redistribute it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required.
|
||||
2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software.
|
||||
3. This notice may not be removed or altered from any source distribution.
|
||||
|
||||
===
|
||||
Third parties
|
||||
===
|
||||
|
||||
raylib - Copyright (c) 2013-2026 Ramon Santamaria (@raysan5)
|
||||
@@ -0,0 +1,70 @@
|
||||
import Foundation
|
||||
|
||||
/// Decode a base64 string back into a regular string
|
||||
func otherBase64ToString(_ encoded: String) -> String {
|
||||
guard
|
||||
let data = Data(base64Encoded: encoded),
|
||||
let str = String(data: data, encoding: .utf8)
|
||||
else {
|
||||
return ""
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
/// Capitalize the first letter of a string
|
||||
func otherCapitalize(_ s: String) -> String {
|
||||
guard let first = s.first else {
|
||||
return s
|
||||
}
|
||||
return String(first).uppercased() + String(s.dropFirst())
|
||||
}
|
||||
|
||||
/// Launch to wait for asynchrnous actions
|
||||
func otherLaunch() {
|
||||
RunLoop.current.run()
|
||||
}
|
||||
|
||||
/// Count leading spaces in a line
|
||||
func otherLineIndent(_ line: String) -> Int {
|
||||
var count = 0
|
||||
for ch in line {
|
||||
if ch == " " {
|
||||
count += 1
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return count
|
||||
}
|
||||
|
||||
/// Print to stderr
|
||||
func otherPrintStderr(_ txt: String) {
|
||||
if let dat = "\(txt)\n".data(using: .utf8) {
|
||||
FileHandle.standardError.write(dat)
|
||||
}
|
||||
}
|
||||
|
||||
/// Print each key/value processed by a controller into stderr
|
||||
func otherSetupConsoleLogging(
|
||||
_ ctrl: DialectController,
|
||||
_ prefix: String
|
||||
) {
|
||||
ctrl.registerCallback { c -> Void in
|
||||
let value = c.fieldAny(c.recentField)
|
||||
let line = "ИГР \(prefix) k/v: '\(c.recentField)'/'\(value)'"
|
||||
otherPrintStderr(line)
|
||||
}
|
||||
}
|
||||
|
||||
/// Write output text to a file
|
||||
func otherWriteFile(
|
||||
_ path: String,
|
||||
_ content: String
|
||||
) {
|
||||
try? content.write(toFile: path, atomically: true, encoding: .utf8)
|
||||
}
|
||||
|
||||
/// Terminate the main RunLoop
|
||||
func otherShutdown() {
|
||||
exit(0)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
import SwiftUI
|
||||
|
||||
struct RootView: View {
|
||||
@ObservedObject var vm: RootVM
|
||||
|
||||
init(_ vm: RootVM) {
|
||||
self.vm = vm
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 16) {
|
||||
Text("Hello, World!")
|
||||
.font(.largeTitle)
|
||||
|
||||
Text(vm.countText)
|
||||
.font(.title3.monospacedDigit())
|
||||
|
||||
Button("Increment") {
|
||||
rootSet(F.didClickIncrement, true)
|
||||
}
|
||||
}
|
||||
.padding(40)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
class RootComponent {
|
||||
let ctrl = DialectController(RootContext())
|
||||
static private(set) weak var singleton: RootComponent?
|
||||
|
||||
init() {
|
||||
Self.singleton = self
|
||||
rootRegisterEffects(ctrl)
|
||||
rootRegisterShoulds(ctrl)
|
||||
}
|
||||
|
||||
func setup() {
|
||||
rootSet(F.didSetup, true)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import Combine
|
||||
|
||||
final class RootVM: ObservableObject {
|
||||
@Published var countText = ""
|
||||
|
||||
static private(set) weak var shared: RootVM?
|
||||
|
||||
init() {
|
||||
Self.shared = self
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
version: 4
|
||||
|
||||
output:
|
||||
game/ver-mac/src/dialect.swift:
|
||||
type: swift
|
||||
|
||||
RootComponent:
|
||||
type: component
|
||||
oneliners:
|
||||
- countText: print(c.countText)
|
||||
- countText: RootVM.shared!.countText = c.countText
|
||||
shoulds:
|
||||
count:
|
||||
1. Upon hitting 10 the first time:
|
||||
if:
|
||||
c.recentField == F.didClickIncrement &&
|
||||
c.count == 7
|
||||
then:
|
||||
c.count += 10
|
||||
2. Upon each button click:
|
||||
if:
|
||||
c.recentField == F.didClickIncrement
|
||||
then:
|
||||
c.count += 1
|
||||
countText:
|
||||
1. Upon each count change:
|
||||
if:
|
||||
c.recentField == F.count
|
||||
then:
|
||||
c.countText = "Count: '\(c.count)'"
|
||||
2. Upon launch:
|
||||
if:
|
||||
c.recentField == F.didLaunch
|
||||
then:
|
||||
c.countText = "Press the button to count"
|
||||
didLaunch:
|
||||
1. Only once during the first setup:
|
||||
if:
|
||||
c.recentField == F.didSetup &&
|
||||
c.didLaunch == false
|
||||
then:
|
||||
c.didLaunch = true
|
||||
|
||||
RootContext:
|
||||
type: context
|
||||
fields:
|
||||
count: Int
|
||||
countText: String
|
||||
didClickIncrement: Bool
|
||||
didLaunch: Bool
|
||||
didSetup: Bool
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>CFBundleName</key>
|
||||
<string>OGS Mahjong</string>
|
||||
<key>CFBundleDisplayName</key>
|
||||
<string>OGS Mahjong</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>org.opengamestudio.Mahjong</string>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>ogs-mahjong</string>
|
||||
<key>CFBundleIconFile</key>
|
||||
<string>AppIcon</string>
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2.0.0</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
<string>11.0</string>
|
||||
<key>NSHighResolutionCapable</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,15 @@
|
||||
// swift-tools-version: 5.9
|
||||
import PackageDescription
|
||||
|
||||
let package = Package(
|
||||
name: "OGSMahjong",
|
||||
platforms: [
|
||||
.macOS(.v13)
|
||||
],
|
||||
targets: [
|
||||
.executableTarget(
|
||||
name: "ogs-mahjong",
|
||||
path: "src"
|
||||
)
|
||||
]
|
||||
)
|
||||
Binary file not shown.
@@ -0,0 +1,22 @@
|
||||
import AppKit
|
||||
|
||||
final class AppDelegate:
|
||||
NSObject,
|
||||
NSApplicationDelegate
|
||||
{
|
||||
let root = RootComponent()
|
||||
let rootVM = RootVM()
|
||||
|
||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||
// Bring the app to the foreground
|
||||
NSApp.setActivationPolicy(.regular)
|
||||
NSApp.activate(ignoringOtherApps: true)
|
||||
|
||||
otherSetupConsoleLogging(root.ctrl, "Root")
|
||||
root.setup()
|
||||
}
|
||||
|
||||
func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
|
||||
true
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
import AppKit
|
||||
import SwiftUI
|
||||
|
||||
@main
|
||||
struct GameApp: App {
|
||||
@NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate
|
||||
|
||||
var body: some Scene {
|
||||
WindowGroup {
|
||||
RootView(appDelegate.rootVM)
|
||||
}
|
||||
.defaultSize(width: 640, height: 480)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,276 @@
|
||||
|
||||
// Reusable "none" constant
|
||||
public let DIALECT_CONTEXT_RECENT_FIELD_NONE = "none"
|
||||
|
||||
// Base protocol for each component's state
|
||||
public protocol DialectContext {
|
||||
var recentField: String { get set }
|
||||
|
||||
func field<T>(_ name: String) -> T
|
||||
func fieldAny(_ name: String) -> Any
|
||||
mutating func setField(_ name: String, _ value: Any)
|
||||
}
|
||||
|
||||
// Default implementation of `fieldAny` method for the protocol
|
||||
public extension DialectContext {
|
||||
func fieldAny(_ name: String) -> Any {
|
||||
return field(name)
|
||||
}
|
||||
}
|
||||
|
||||
// The engine of the dialect components
|
||||
public class DialectController {
|
||||
var callbacks = [(DialectContext) -> Void]()
|
||||
var context: DialectContext
|
||||
var functions = [(DialectContext) -> DialectContext]()
|
||||
var isProcessingQueue = false
|
||||
var queue = [DialectContext]()
|
||||
|
||||
public init(_ context: DialectContext) {
|
||||
self.context = context
|
||||
}
|
||||
|
||||
func executeFunctions() {
|
||||
let c = queue.removeFirst()
|
||||
// Keep SSOT: Only allow single field change per should-function
|
||||
context.recentField = c.recentField
|
||||
context.setField(c.recentField, c.fieldAny(c.recentField))
|
||||
|
||||
for f in functions {
|
||||
let ctx = f(context)
|
||||
if ctx.recentField != DIALECT_CONTEXT_RECENT_FIELD_NONE {
|
||||
queue.append(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
reportContext()
|
||||
}
|
||||
|
||||
func processQueue() {
|
||||
// Prevent recursion.
|
||||
if isProcessingQueue {
|
||||
return
|
||||
}
|
||||
|
||||
isProcessingQueue = true
|
||||
|
||||
while (queue.count > 0) {
|
||||
executeFunctions()
|
||||
}
|
||||
|
||||
isProcessingQueue = false
|
||||
}
|
||||
|
||||
public func registerCallback(_ cb: @escaping (DialectContext) -> Void) {
|
||||
callbacks.append(cb)
|
||||
}
|
||||
|
||||
public func registerFieldCallback(
|
||||
_ fieldName: String,
|
||||
_ cb: @escaping (DialectContext) -> Void
|
||||
) {
|
||||
callbacks.append({ c in
|
||||
if c.recentField == fieldName {
|
||||
cb(c)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
public func registerFunction(_ f: @escaping (DialectContext) -> DialectContext) {
|
||||
functions.append(f)
|
||||
}
|
||||
|
||||
func reportContext() {
|
||||
for cb in callbacks {
|
||||
cb(context)
|
||||
}
|
||||
}
|
||||
|
||||
public func set(
|
||||
_ fieldName: String,
|
||||
_ value: Any
|
||||
) {
|
||||
var c = context
|
||||
c.setField(fieldName, value)
|
||||
c.recentField = fieldName
|
||||
queue.append(c)
|
||||
processQueue()
|
||||
}
|
||||
}
|
||||
|
||||
// Register several oneliner callbacks to a controller
|
||||
func registerOneliners<T>(
|
||||
_ ctrl: DialectController,
|
||||
_ items: [Any]
|
||||
) -> T? {
|
||||
let halfCount = items.count / 2
|
||||
for i in 0..<halfCount {
|
||||
let field = items[i * 2] as! String
|
||||
let callback = items[i * 2 + 1] as! (T) -> Void
|
||||
ctrl.registerFieldCallback(field) { cc in
|
||||
let c = cc as! T
|
||||
callback(c)
|
||||
}
|
||||
}
|
||||
|
||||
// A hack for generics to operate
|
||||
return nil
|
||||
}
|
||||
|
||||
// Context field names for static type check
|
||||
public struct F {
|
||||
public static let count = "count"
|
||||
public static let countText = "countText"
|
||||
public static let didClickIncrement = "didClickIncrement"
|
||||
public static let didLaunch = "didLaunch"
|
||||
public static let didSetup = "didSetup"
|
||||
|
||||
}
|
||||
public struct RootContext: DialectContext {
|
||||
public var count = Int()
|
||||
public var countText = String()
|
||||
public var didClickIncrement = Bool()
|
||||
public var didLaunch = Bool()
|
||||
public var didSetup = Bool()
|
||||
|
||||
public var recentField = ""
|
||||
|
||||
public func field<T>(_ name: String) -> T {
|
||||
if (name == "count") {
|
||||
return count as! T
|
||||
}
|
||||
else if (name == "countText") {
|
||||
return countText as! T
|
||||
}
|
||||
else if (name == "didClickIncrement") {
|
||||
return didClickIncrement as! T
|
||||
}
|
||||
else if (name == "didLaunch") {
|
||||
return didLaunch as! T
|
||||
}
|
||||
else if (name == "didSetup") {
|
||||
return didSetup as! T
|
||||
}
|
||||
|
||||
return "unknown-field-name" as! T
|
||||
}
|
||||
|
||||
public mutating func setField(
|
||||
_ name: String,
|
||||
_ value: Any
|
||||
) {
|
||||
if (name == "count") {
|
||||
count = value as! Int
|
||||
}
|
||||
else if (name == "countText") {
|
||||
countText = value as! String
|
||||
}
|
||||
else if (name == "didClickIncrement") {
|
||||
didClickIncrement = value as! Bool
|
||||
}
|
||||
else if (name == "didLaunch") {
|
||||
didLaunch = value as! Bool
|
||||
}
|
||||
else if (name == "didSetup") {
|
||||
didSetup = value as! Bool
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public func rootSet(
|
||||
_ key: String,
|
||||
_ value: Any
|
||||
) {
|
||||
RootComponent.singleton!.ctrl.set(key, value)
|
||||
}
|
||||
|
||||
func rootShouldResetCount(_ c: RootContext) -> RootContext {
|
||||
var c = c
|
||||
|
||||
/* 1. Upon hitting 10 the first time */
|
||||
if
|
||||
c.recentField == F.didClickIncrement &&
|
||||
c.count == 7
|
||||
{
|
||||
c.count += 10
|
||||
c.recentField = F.count
|
||||
return c
|
||||
}
|
||||
|
||||
/* 2. Upon each button click */
|
||||
if
|
||||
c.recentField == F.didClickIncrement
|
||||
{
|
||||
c.count += 1
|
||||
c.recentField = F.count
|
||||
return c
|
||||
}
|
||||
|
||||
|
||||
c.recentField = DIALECT_CONTEXT_RECENT_FIELD_NONE
|
||||
return c
|
||||
}
|
||||
|
||||
func rootShouldResetCountText(_ c: RootContext) -> RootContext {
|
||||
var c = c
|
||||
|
||||
/* 1. Upon each count change */
|
||||
if
|
||||
c.recentField == F.count
|
||||
{
|
||||
c.countText = "Count: '\(c.count)'"
|
||||
c.recentField = F.countText
|
||||
return c
|
||||
}
|
||||
|
||||
/* 2. Upon launch */
|
||||
if
|
||||
c.recentField == F.didLaunch
|
||||
{
|
||||
c.countText = "Press the button to count"
|
||||
c.recentField = F.countText
|
||||
return c
|
||||
}
|
||||
|
||||
|
||||
c.recentField = DIALECT_CONTEXT_RECENT_FIELD_NONE
|
||||
return c
|
||||
}
|
||||
|
||||
func rootShouldResetDidLaunch(_ c: RootContext) -> RootContext {
|
||||
var c = c
|
||||
|
||||
/* 1. Only once during the first setup */
|
||||
if
|
||||
c.recentField == F.didSetup &&
|
||||
c.didLaunch == false
|
||||
{
|
||||
c.didLaunch = true
|
||||
c.recentField = F.didLaunch
|
||||
return c
|
||||
}
|
||||
|
||||
|
||||
c.recentField = DIALECT_CONTEXT_RECENT_FIELD_NONE
|
||||
return c
|
||||
}
|
||||
|
||||
func rootRegisterShoulds(_ ctrl: DialectController) {
|
||||
[
|
||||
rootShouldResetCount,
|
||||
rootShouldResetCountText,
|
||||
rootShouldResetDidLaunch,
|
||||
|
||||
].forEach { f in
|
||||
ctrl.registerFunction { c in f(c as! RootContext) }
|
||||
}
|
||||
}
|
||||
|
||||
func rootRegisterEffects(_ ctrl: DialectController) {
|
||||
let _: RootContext? = registerOneliners(ctrl, [
|
||||
F.countText, { (c: RootContext) in print(c.countText) },
|
||||
F.countText, { (c: RootContext) in RootVM.shared!.countText = c.countText },
|
||||
|
||||
])
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../../../components/other/other.swift
|
||||
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../../../components/root/root.swift
|
||||
+1
@@ -0,0 +1 @@
|
||||
../../../../components/root/rootVM.swift
|
||||
@@ -0,0 +1 @@
|
||||
../../../../../components/root/mac/view/RootView.swift
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Symlink
+1
@@ -0,0 +1 @@
|
||||
../../swiftraylib
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
#!/bin/bash -e
|
||||
SDIR=$(cd "$(dirname "$0")" ; pwd -P)
|
||||
|
||||
T=$SDIR/step
|
||||
source $SDIR/paths
|
||||
|
||||
STEP=0
|
||||
source $T/generateDialect
|
||||
source $T/buildMac
|
||||
source $T/packageMac
|
||||
@@ -0,0 +1 @@
|
||||
APP_MAC="$SDIR/../game/ver-mac/.build/OGS Mahjong.app"
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
killall cfprefsd 2>/dev/null || true
|
||||
sleep 1
|
||||
|
||||
rm -f $HOME/Library/Preferences/OGS\ Mahjong.plist \
|
||||
$HOME/Library/Preferences/org.opengamestudio.Mahjong.plist
|
||||
|
||||
echo "removed app preferences (window geometry)"
|
||||
Executable
+8
@@ -0,0 +1,8 @@
|
||||
#!/bin/bash -e
|
||||
SDIR=$(cd "$(dirname "$0")" ; pwd -P)
|
||||
|
||||
T=$SDIR/step
|
||||
source $SDIR/paths
|
||||
|
||||
STEP=0
|
||||
source $T/runMac
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
STEP=$((STEP+1))
|
||||
echo -e "\n> > > > Step №$STEP. Build for macOS"
|
||||
|
||||
cd $SDIR/../game/ver-mac
|
||||
swift build # -Xswiftc -swift-version -Xswiftc 6
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
STEP=$((STEP+1))
|
||||
echo -e "\n> > > > Step №$STEP. Generate Dialect"
|
||||
|
||||
$SDIR/../../dialect/util/run-generator --file=$SDIR/../dialect.yml
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
STEP=$((STEP+1))
|
||||
echo -e "\n> > > > Step №$STEP. Make an app package for macOS"
|
||||
|
||||
rm -rf "$APP_MAC"
|
||||
|
||||
APP_DAT="$APP_MAC/Contents"
|
||||
|
||||
mkdir -p "$APP_DAT/MacOS"
|
||||
mkdir -p "$APP_DAT/Resources"
|
||||
|
||||
cp "$SDIR/../game/ver-mac/.build/debug/ogs-mahjong" "$APP_DAT/MacOS/ogs-mahjong"
|
||||
cp "$SDIR/../game/ver-mac/Info.plist" "$APP_DAT/Info.plist"
|
||||
cp "$SDIR/../game/ver-mac/Resources/AppIcon.icns" "$APP_DAT/Resources/AppIcon.icns"
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
#!/bin/bash -e
|
||||
|
||||
STEP=$((STEP+1))
|
||||
echo -e "\n> > > > Step №$STEP. Run macOS app"
|
||||
|
||||
"$APP_MAC/Contents/MacOS/ogs-mahjong"
|
||||
Reference in new issue
Block a user