forked from MeloNX/MeloNX
New UI Interface
This commit is contained in:
parent
fdbcc483b3
commit
e81ee8f8bf
@ -526,6 +526,7 @@
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UIRequiresFullScreen = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
||||
INFOPLIST_KEY_UISupportsDocumentBrowser = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
@ -668,6 +669,7 @@
|
||||
INFOPLIST_KEY_UILaunchScreen_Generation = YES;
|
||||
INFOPLIST_KEY_UIRequiresFullScreen = YES;
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
|
||||
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait";
|
||||
INFOPLIST_KEY_UISupportsDocumentBrowser = YES;
|
||||
IPHONEOS_DEPLOYMENT_TARGET = 16.0;
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
|
Binary file not shown.
@ -123,13 +123,8 @@ class VirtualController {
|
||||
}
|
||||
|
||||
func thumbstickMoved(_ stick: ThumbstickType, x: Double, y: Double) {
|
||||
// Convert float values (-1.0 to 1.0) to SDL axis values (-32768 to 32767)
|
||||
var scaleFactor = 32767.0
|
||||
if UIDevice.current.systemName.contains("iPadOS") {
|
||||
scaleFactor /= (160 * 1.2)
|
||||
} else {
|
||||
scaleFactor /= 160
|
||||
}
|
||||
var scaleFactor = 32767.0 / 160
|
||||
|
||||
let scaledX = Int16(min(32767.0, max(-32768.0, x * scaleFactor)))
|
||||
let scaledY = Int16(min(32767.0, max(-32768.0, y * scaleFactor)))
|
||||
|
||||
|
@ -39,7 +39,7 @@ class Ryujinx {
|
||||
|
||||
private init() {}
|
||||
|
||||
public struct Configuration : Codable {
|
||||
public struct Configuration : Codable, Equatable {
|
||||
var gamepath: String
|
||||
var inputids: [String]
|
||||
var resscale: Float
|
||||
|
24
src/MeloNX/MeloNX/Models/Game.swift
Normal file
24
src/MeloNX/MeloNX/Models/Game.swift
Normal file
@ -0,0 +1,24 @@
|
||||
//
|
||||
// GameInfo.swift
|
||||
// MeloNX
|
||||
//
|
||||
// Created by Stossy11 on 9/12/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
public struct Game: Identifiable, Equatable {
|
||||
public var id = UUID()
|
||||
|
||||
var containerFolder: URL
|
||||
var fileType: UTType
|
||||
|
||||
var fileURL: URL
|
||||
|
||||
var titleName: String
|
||||
var titleId: String
|
||||
var developer: String
|
||||
var version: String
|
||||
var icon: Image?
|
||||
}
|
@ -10,6 +10,7 @@ import SwiftUI
|
||||
import GameController
|
||||
import Darwin
|
||||
import UIKit
|
||||
import MetalKit
|
||||
// import SDL
|
||||
|
||||
struct MoltenVKSettings: Codable, Hashable {
|
||||
@ -55,12 +56,10 @@ struct ContentView: View {
|
||||
|
||||
// MARK: - Body
|
||||
var body: some View {
|
||||
iOSNav {
|
||||
if let game {
|
||||
emulationView
|
||||
} else {
|
||||
mainMenuView
|
||||
}
|
||||
if let game {
|
||||
emulationView
|
||||
} else {
|
||||
mainMenuView
|
||||
}
|
||||
}
|
||||
|
||||
@ -73,53 +72,10 @@ struct ContentView: View {
|
||||
}
|
||||
|
||||
private var mainMenuView: some View {
|
||||
HStack {
|
||||
GameListView(startemu: $game)
|
||||
.onAppear {
|
||||
refreshControllersList()
|
||||
}
|
||||
|
||||
settingsListView
|
||||
}
|
||||
}
|
||||
|
||||
private var settingsListView: some View {
|
||||
List {
|
||||
Section("Settings") {
|
||||
NavigationLink("Config") {
|
||||
SettingsView(config: $config, MoltenVKSettings: $settings)
|
||||
.onAppear() {
|
||||
virtualController?.disconnect()
|
||||
}
|
||||
}
|
||||
MainTabView(startemu: $game, config: $config, MVKconfig: $settings, controllersList: $controllersList, currentControllers: $currentControllers, onscreencontroller: $onscreencontroller)
|
||||
.onAppear() {
|
||||
refreshControllersList()
|
||||
}
|
||||
|
||||
|
||||
Section {
|
||||
Button("Refresh", action: refreshControllersList)
|
||||
ForEach(controllersList, id: \.self) { controller in
|
||||
controllerRow(for: controller)
|
||||
}
|
||||
} header: {
|
||||
Text("Controllers")
|
||||
} footer: {
|
||||
Text("If no controllers are selected, the keyboard will be used.")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func controllerRow(for controller: Controller) -> some View {
|
||||
HStack {
|
||||
Button(controller.name) {
|
||||
toggleController(controller)
|
||||
}
|
||||
Spacer()
|
||||
if currentControllers.contains(where: { $0.id == controller.id }) {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -155,36 +111,24 @@ struct ContentView: View {
|
||||
|
||||
|
||||
}
|
||||
// controllerCallback!()
|
||||
}
|
||||
|
||||
private func refreshControllersList() {
|
||||
Timer.scheduledTimer(withTimeInterval: 0.5, repeats: false) { _ in
|
||||
controllersList = Ryujinx.shared.getConnectedControllers()
|
||||
|
||||
if let onscreen = controllersList.first(where: { $0.name == Ryujinx.shared.virtualController.controllername }) {
|
||||
self.onscreencontroller = onscreen
|
||||
}
|
||||
|
||||
controllersList.removeAll(where: { $0.id == "0"})
|
||||
|
||||
if controllersList.count > 2 {
|
||||
let controller = controllersList[2]
|
||||
currentControllers.append(controller)
|
||||
} else if let controller = controllersList.first(where: { $0.id == onscreencontroller.id }), !controllersList.isEmpty {
|
||||
currentControllers.append(controller)
|
||||
}
|
||||
controllersList = Ryujinx.shared.getConnectedControllers()
|
||||
|
||||
if let onscreen = controllersList.first(where: { $0.name == Ryujinx.shared.virtualController.controllername }) {
|
||||
self.onscreencontroller = onscreen
|
||||
}
|
||||
}
|
||||
|
||||
private func toggleController(_ controller: Controller) {
|
||||
if currentControllers.contains(where: { $0.id == controller.id }) {
|
||||
currentControllers.removeAll(where: { $0.id == controller.id })
|
||||
} else {
|
||||
|
||||
controllersList.removeAll(where: { $0.id == "0"})
|
||||
|
||||
if controllersList.count > 2 {
|
||||
let controller = controllersList[2]
|
||||
currentControllers.append(controller)
|
||||
} else if let controller = controllersList.first(where: { $0.id == onscreencontroller.id }), !controllersList.isEmpty {
|
||||
currentControllers.append(controller)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func showAlert(title: String, message: String, showOk: Bool, completion: @escaping (Bool) -> Void) {
|
||||
DispatchQueue.main.async {
|
||||
|
@ -64,12 +64,12 @@ struct ControllerView: View {
|
||||
// Spacer()
|
||||
VStack {
|
||||
// Spacer()
|
||||
ButtonView(button: .start) // Adding the + button
|
||||
ButtonView(button: .back) // Adding the + button
|
||||
}
|
||||
Spacer()
|
||||
VStack {
|
||||
// Spacer()
|
||||
ButtonView(button: .back) // Adding the - button
|
||||
ButtonView(button: .start) // Adding the - button
|
||||
}
|
||||
// Spacer()
|
||||
}
|
||||
|
@ -5,41 +5,176 @@
|
||||
// Created by Stossy11 on 3/11/2024.
|
||||
//
|
||||
|
||||
// MARK: - This will most likely not be used in prod
|
||||
import SwiftUI
|
||||
import UniformTypeIdentifiers
|
||||
|
||||
public struct Game: Identifiable, Equatable {
|
||||
public var id = UUID()
|
||||
|
||||
var containerFolder: URL
|
||||
var fileType: UTType
|
||||
struct MainTabView: View {
|
||||
@Binding var startemu: URL?
|
||||
@Binding var config: Ryujinx.Configuration
|
||||
@Binding var MVKconfig: [MoltenVKSettings]
|
||||
@Binding var controllersList: [Controller]
|
||||
@Binding var currentControllers: [Controller]
|
||||
|
||||
var fileURL: URL
|
||||
|
||||
var titleName: String
|
||||
var titleId: String
|
||||
var developer: String
|
||||
var version: String
|
||||
var icon: Image?
|
||||
@Binding var onscreencontroller: Controller
|
||||
|
||||
var body: some View {
|
||||
TabView {
|
||||
GameLibraryView(startemu: $startemu)
|
||||
.tabItem {
|
||||
Label("Games", systemImage: "gamecontroller.fill")
|
||||
}
|
||||
|
||||
SelectControllerView(controllersList: $controllersList, currentControllers: $currentControllers, onscreencontroller: $onscreencontroller)
|
||||
.tabItem {
|
||||
Label("Controllers", systemImage: "gamecontroller.fill")
|
||||
}
|
||||
|
||||
SettingsView(config: $config, MoltenVKSettings: $MVKconfig)
|
||||
.tabItem {
|
||||
Label("Settings", systemImage: "gear")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct GameListView: View {
|
||||
struct GameLibraryView: View {
|
||||
@Binding var startemu: URL?
|
||||
@State private var games: [Game] = []
|
||||
|
||||
@State private var searchText = ""
|
||||
@State private var isSearching = false
|
||||
@AppStorage("recentGames") private var recentGamesData: Data = Data()
|
||||
@State private var recentGames: [Game] = []
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
var filteredGames: [Game] {
|
||||
if searchText.isEmpty {
|
||||
return games
|
||||
}
|
||||
return games.filter {
|
||||
$0.titleName.localizedCaseInsensitiveContains(searchText) ||
|
||||
$0.developer.localizedCaseInsensitiveContains(searchText)
|
||||
}
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
List($games, id: \.id) { $game in
|
||||
Button {
|
||||
startemu = $game.wrappedValue.fileURL
|
||||
} label: {
|
||||
Text(game.titleName)
|
||||
iOSNav {
|
||||
ScrollView {
|
||||
LazyVStack(alignment: .leading, spacing: 20) {
|
||||
if !isSearching {
|
||||
Text("Games")
|
||||
.font(.system(size: 34, weight: .bold))
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 12)
|
||||
}
|
||||
|
||||
if games.isEmpty {
|
||||
VStack(spacing: 16) {
|
||||
Image(systemName: "gamecontroller.fill")
|
||||
.font(.system(size: 64))
|
||||
.foregroundColor(.secondary.opacity(0.7))
|
||||
.padding(.top, 60)
|
||||
Text("No Games Found")
|
||||
.font(.title2.bold())
|
||||
.foregroundColor(.primary)
|
||||
Text("Add ROM, Keys and Firmware to get started")
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.top, 40)
|
||||
} else {
|
||||
if !isSearching && !recentGames.isEmpty {
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text("Recent")
|
||||
.font(.title2.bold())
|
||||
.padding(.horizontal)
|
||||
|
||||
ScrollView(.horizontal, showsIndicators: false) {
|
||||
LazyHStack(spacing: 16) {
|
||||
ForEach(recentGames) { game in
|
||||
RecentGameCard(game: game, startemu: $startemu)
|
||||
.onTapGesture {
|
||||
addToRecentGames(game)
|
||||
startemu = game.fileURL
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 12) {
|
||||
Text("All Games")
|
||||
.font(.title2.bold())
|
||||
.padding(.horizontal)
|
||||
|
||||
LazyVStack(spacing: 2) {
|
||||
ForEach(filteredGames) { game in
|
||||
GameListRow(game: game, startemu: $startemu)
|
||||
.onTapGesture {
|
||||
addToRecentGames(game)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LazyVStack(spacing: 2) {
|
||||
ForEach(filteredGames) { game in
|
||||
GameListRow(game: game, startemu: $startemu)
|
||||
.onTapGesture {
|
||||
addToRecentGames(game)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
loadGames()
|
||||
loadRecentGames()
|
||||
}
|
||||
}
|
||||
}
|
||||
.navigationTitle("Games")
|
||||
.onAppear(perform: loadGames)
|
||||
.background(Color(.systemGroupedBackground))
|
||||
.searchable(text: $searchText)
|
||||
.onChange(of: searchText) { _ in
|
||||
isSearching = !searchText.isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func addToRecentGames(_ game: Game) {
|
||||
recentGames.removeAll { $0.id == game.id }
|
||||
|
||||
recentGames.insert(game, at: 0)
|
||||
|
||||
if recentGames.count > 5 {
|
||||
recentGames = Array(recentGames.prefix(5))
|
||||
}
|
||||
|
||||
saveRecentGames()
|
||||
}
|
||||
|
||||
private func saveRecentGames() {
|
||||
do {
|
||||
let encoder = JSONEncoder()
|
||||
let data = try encoder.encode(recentGames)
|
||||
recentGamesData = data
|
||||
} catch {
|
||||
print("Error saving recent games: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
private func loadRecentGames() {
|
||||
do {
|
||||
let decoder = JSONDecoder()
|
||||
recentGames = try decoder.decode([Game].self, from: recentGamesData)
|
||||
} catch {
|
||||
print("Error loading recent games: \(error)")
|
||||
recentGames = []
|
||||
}
|
||||
}
|
||||
|
||||
private func loadGames() {
|
||||
let fileManager = FileManager.default
|
||||
guard let documentsDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
|
||||
@ -54,7 +189,7 @@ struct GameListView: View {
|
||||
print("Failed to create roms directory: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
games = []
|
||||
// Load games only from "roms" folder
|
||||
do {
|
||||
let files = try fileManager.contentsOfDirectory(at: romsDirectory, includingPropertiesForKeys: nil)
|
||||
@ -98,3 +233,147 @@ struct GameListView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make sure your Game model conforms to Codable
|
||||
extension Game: Codable {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case titleName, titleId, developer, version, fileURL
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
titleName = try container.decode(String.self, forKey: .titleName)
|
||||
titleId = try container.decode(String.self, forKey: .titleId)
|
||||
developer = try container.decode(String.self, forKey: .developer)
|
||||
version = try container.decode(String.self, forKey: .version)
|
||||
fileURL = try container.decode(URL.self, forKey: .fileURL)
|
||||
|
||||
// Initialize other properties
|
||||
self.containerFolder = fileURL.deletingLastPathComponent()
|
||||
self.fileType = .item
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: CodingKeys.self)
|
||||
try container.encode(titleName, forKey: .titleName)
|
||||
try container.encode(titleId, forKey: .titleId)
|
||||
try container.encode(developer, forKey: .developer)
|
||||
try container.encode(version, forKey: .version)
|
||||
try container.encode(fileURL, forKey: .fileURL)
|
||||
}
|
||||
}
|
||||
|
||||
struct RecentGameCard: View {
|
||||
let game: Game
|
||||
@Binding var startemu: URL?
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
var body: some View {
|
||||
Button(action: {
|
||||
startemu = game.fileURL
|
||||
}) {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
if let icon = game.icon {
|
||||
icon
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.frame(width: 140, height: 140)
|
||||
.cornerRadius(12)
|
||||
} else {
|
||||
ZStack {
|
||||
RoundedRectangle(cornerRadius: 12)
|
||||
.fill(colorScheme == .dark ?
|
||||
Color(.systemGray5) : Color(.systemGray6))
|
||||
.frame(width: 140, height: 140)
|
||||
|
||||
Image(systemName: "gamecontroller.fill")
|
||||
.font(.system(size: 40))
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(game.titleName)
|
||||
.font(.subheadline.bold())
|
||||
.lineLimit(1)
|
||||
|
||||
Text(game.developer)
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
.padding(.horizontal, 4)
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
|
||||
struct GameListRow: View {
|
||||
let game: Game
|
||||
@Binding var startemu: URL?
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
var body: some View {
|
||||
Button(action: {
|
||||
startemu = game.fileURL
|
||||
}) {
|
||||
HStack(spacing: 16) {
|
||||
// Game Icon
|
||||
if let icon = game.icon {
|
||||
icon
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.frame(width: 45, height: 45)
|
||||
.cornerRadius(8)
|
||||
} else {
|
||||
ZStack {
|
||||
RoundedRectangle(cornerRadius: 8)
|
||||
.fill(colorScheme == .dark ?
|
||||
Color(.systemGray5) : Color(.systemGray6))
|
||||
.frame(width: 45, height: 45)
|
||||
|
||||
Image(systemName: "gamecontroller.fill")
|
||||
.font(.system(size: 20))
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
|
||||
// Game Info
|
||||
VStack(alignment: .leading, spacing: 2) {
|
||||
Text(game.titleName)
|
||||
.font(.body)
|
||||
.foregroundColor(.primary)
|
||||
|
||||
Text(game.developer)
|
||||
.font(.subheadline)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
|
||||
Spacer()
|
||||
|
||||
Image(systemName: "play.circle.fill")
|
||||
.font(.title2)
|
||||
.foregroundColor(.accentColor)
|
||||
.opacity(0.8)
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 8)
|
||||
.background(Color(.systemBackground))
|
||||
.contextMenu {
|
||||
Button {
|
||||
startemu = game.fileURL
|
||||
} label: {
|
||||
Label("Play Now", systemImage: "play.fill")
|
||||
}
|
||||
|
||||
Button {
|
||||
// Add info action
|
||||
} label: {
|
||||
Label("Game Info", systemImage: "info.circle")
|
||||
}
|
||||
}
|
||||
}
|
||||
.buttonStyle(.plain)
|
||||
}
|
||||
}
|
||||
|
@ -1,87 +0,0 @@
|
||||
//
|
||||
// VulkanSDLView.swift
|
||||
// MeloNX
|
||||
//
|
||||
// Created by Stossy11 on 3/11/2024.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import MetalKit
|
||||
|
||||
/*
|
||||
class SDLView: UIView {
|
||||
var sdlwin: OpaquePointer?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
DispatchQueue.main.async { [self] in
|
||||
makeSDLWindow()
|
||||
}
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
super.init(coder: coder)
|
||||
DispatchQueue.main.async { [self] in
|
||||
makeSDLWindow()
|
||||
}
|
||||
}
|
||||
|
||||
func getWindowFlags() -> UInt32 {
|
||||
return SDL_WINDOW_VULKAN.rawValue
|
||||
}
|
||||
|
||||
private func makeSDLWindow() {
|
||||
let width: Int32 = 1280 // Replace with the desired width
|
||||
let height: Int32 = 720 // Replace with the desired height
|
||||
|
||||
let defaultFlags: UInt32 = SDL_WINDOW_SHOWN.rawValue
|
||||
let fullscreenFlag: UInt32 = SDL_WINDOW_FULLSCREEN.rawValue // Or SDL_WINDOW_FULLSCREEN_DESKTOP if needed
|
||||
|
||||
// Create the SDL window
|
||||
sdlwin = SDL_CreateWindow(
|
||||
"Ryujinx",
|
||||
0,
|
||||
0,
|
||||
width,
|
||||
height,
|
||||
defaultFlags | getWindowFlags() // | fullscreenFlag | getWindowFlags()
|
||||
)
|
||||
|
||||
// Check if we successfully retrieved the SDL window
|
||||
guard sdlwin != nil else {
|
||||
print("Error creating SDL window: \(String(cString: SDL_GetError()))")
|
||||
return
|
||||
}
|
||||
|
||||
print("SDL window created successfully.")
|
||||
|
||||
// Position SDL window over this UIView
|
||||
self.syncSDLWindowPosition()
|
||||
}
|
||||
|
||||
private func syncSDLWindowPosition() {
|
||||
guard let sdlwin = sdlwin else { return }
|
||||
|
||||
|
||||
// Get the frame of the UIView in screen coordinates
|
||||
let viewFrameInWindow = self.convert(self.bounds, to: nil)
|
||||
|
||||
// Set the SDL window position and size to match the UIView frame
|
||||
SDL_SetWindowPosition(sdlwin, Int32(viewFrameInWindow.origin.x), Int32(viewFrameInWindow.origin.y))
|
||||
SDL_SetWindowSize(sdlwin, Int32(viewFrameInWindow.width), Int32(viewFrameInWindow.height))
|
||||
|
||||
// Bring SDL window to the front
|
||||
SDL_RaiseWindow(sdlwin)
|
||||
|
||||
print("SDL window positioned over SDLView.")
|
||||
}
|
||||
|
||||
override func layoutSubviews() {
|
||||
super.layoutSubviews()
|
||||
// Adjust SDL window whenever layout changes
|
||||
syncSDLWindowPosition()
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
@ -1,28 +0,0 @@
|
||||
//
|
||||
// VulkanSDLViewRepresentable.swift
|
||||
// MeloNX
|
||||
//
|
||||
// Created by Stossy11 on 3/11/2024.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import SwiftUI
|
||||
import GameController
|
||||
/*
|
||||
struct SDLViewRepresentable: UIViewRepresentable {
|
||||
let configure: (UInt32) -> Void
|
||||
func makeUIView(context: Context) -> SDLView {
|
||||
// Configure (start ryu) before initialsing SDLView so SDLView can get the SDL_Window from Ryu
|
||||
let view = SDLView(frame: .zero)
|
||||
configure(SDL_GetWindowID(view.sdlwin))
|
||||
return view
|
||||
|
||||
}
|
||||
|
||||
func updateUIView(_ uiView: SDLView, context: Context) {
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
*/
|
||||
|
@ -0,0 +1,53 @@
|
||||
//
|
||||
// SelectControllerView.swift
|
||||
// MeloNX
|
||||
//
|
||||
// Created by Stossy11 on 9/12/2024.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
struct SelectControllerView: View {
|
||||
|
||||
@Binding var controllersList: [Controller]
|
||||
@Binding var currentControllers: [Controller]
|
||||
|
||||
@Binding var onscreencontroller: Controller
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
|
||||
Section {
|
||||
ForEach(controllersList, id: \.self) { controller in
|
||||
controllerRow(for: controller)
|
||||
}
|
||||
} footer: {
|
||||
Text("If no controllers are selected, the keyboard will be used.")
|
||||
.font(.footnote)
|
||||
.foregroundColor(.gray)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func controllerRow(for controller: Controller) -> some View {
|
||||
HStack {
|
||||
Button(controller.name) {
|
||||
toggleController(controller)
|
||||
}
|
||||
Spacer()
|
||||
if currentControllers.contains(where: { $0.id == controller.id }) {
|
||||
Image(systemName: "checkmark.circle.fill")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func toggleController(_ controller: Controller) {
|
||||
if currentControllers.contains(where: { $0.id == controller.id }) {
|
||||
currentControllers.removeAll(where: { $0.id == controller.id })
|
||||
} else {
|
||||
currentControllers.append(controller)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
@ -44,7 +44,6 @@ struct SettingsView: View {
|
||||
|
||||
Section(header: Title("Input Settings")) {
|
||||
Toggle("List Input IDs", isOn: $config.listinputids)
|
||||
Toggle("Nintendo Controller Layout", isOn: $config.nintendoinput)
|
||||
Toggle("Ryujinx Demo On-Screen Controller", isOn: $ryuDemo)
|
||||
// Toggle("Host Mapped Memory", isOn: $config.hostMappedMemory)
|
||||
}
|
||||
@ -89,10 +88,10 @@ struct SettingsView: View {
|
||||
print(configs)
|
||||
}
|
||||
}
|
||||
.navigationTitle("Settings")
|
||||
.navigationBarItems(trailing: Button("Save") {
|
||||
.onChange(of: config) { newValue in
|
||||
print(newValue)
|
||||
saveSettings()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func saveSettings() {
|
||||
|
@ -63,8 +63,16 @@ namespace Ryujinx.Common.Configuration
|
||||
{
|
||||
appDataPath = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile);
|
||||
}
|
||||
string userProfilePath;
|
||||
if (OperatingSystem.IsIOS())
|
||||
{
|
||||
userProfilePath = appDataPath;
|
||||
}
|
||||
else
|
||||
{
|
||||
userProfilePath = Path.Combine(appDataPath, DefaultBaseDir);
|
||||
}
|
||||
|
||||
string userProfilePath = Path.Combine(appDataPath, DefaultBaseDir);
|
||||
string portablePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, DefaultPortableDir);
|
||||
|
||||
if (Directory.Exists(portablePath))
|
||||
|
@ -116,10 +116,10 @@ private void CreateFonts(string uiThemeFontFamily)
|
||||
if (OperatingSystem.IsIOS())
|
||||
{
|
||||
availableFonts = new string[] {
|
||||
"Chalkboard",
|
||||
"Chalkboard", // San Francisco is the default font on iOS
|
||||
"Chalkboard", // Legacy iOS font
|
||||
"Chalkboard" // Common system font
|
||||
"SF Pro",
|
||||
"New York",
|
||||
"Helvetica Neue",
|
||||
"Avenir"
|
||||
};
|
||||
}
|
||||
else
|
||||
|
@ -958,7 +958,6 @@ namespace Ryujinx.Headless.SDL2
|
||||
|
||||
static void Load(Options option)
|
||||
{
|
||||
AppDataManager.Initialize(option.BaseDataDir);
|
||||
|
||||
if (_virtualFileSystem == null)
|
||||
{
|
||||
|
Loading…
x
Reference in New Issue
Block a user