feat(mac): added tabview and webview

This commit is contained in:
Gabriel Schantz 2026-08-03 15:37:35 -04:00
parent 5c59da838b
commit 2b2fc15da3
4 changed files with 102 additions and 35 deletions

View file

@ -0,0 +1,34 @@
/*
* Keyman is copyright (C) SIL Global. MIT License.
*
* Created by Gabriel Schantz on 2026-08-03
*
* Webview used to show help for Keyman keyboards
*/
import Foundation
import SwiftUI
import WebKit
import KeymanSettings
public struct HelpView: NSViewRepresentable {
@EnvironmentObject var settings: SettingsContainer
let helpFileURL: URL
// create the AppKit view instance
public func makeNSView(context: Context) -> WKWebView {
let webView = WKWebView()
return webView
}
// update the view when SwiftUI state changes
public func updateNSView(_ nsView: WKWebView, context: Context) {
let request = URLRequest(url: helpFileURL)
// only load the request if it's not already loading/loaded to prevent infinite loops
if nsView.url != helpFileURL {
nsView.load(request)
}
}
}

View file

@ -4,8 +4,7 @@
* Created by Gabriel Schantz on 2026-06-29
*
* Main view used for configuring Keyman
* FEAT/MAC/CONFIG-WINDOW TODO: Finish writing file summary
* FEAT/MAC/CONFIG-WINDOW TODO: Set width and height for window
* FEAT/MAC/CONFIG-WINDOW TODO: Set default width and height for window
*/
import SwiftUI
@ -17,43 +16,68 @@ struct MainConfigView: View {
// visibilty state for the add package sheet
@State private var isShowingSheet = false
// used to identify the expanded KeymanPackage id
// both single and multi package views share the same state variable so only single disclosure group is expanded at once
// both single and multi package views share the same state variable so only a single disclosure group is expanded at once
@State private var expandedPackageID: UUID? = nil
@State private var selectedTab = 0
@State private var packageSelectedForHelpUrl: URL? = nil
/**
* Assigns packageSelectedForHelpUrl the url argument and changes the selected tab to the help tab
*/
public func showHelpTab(for url: URL) {
packageSelectedForHelpUrl = url
selectedTab = 1
}
var body: some View {
VStack {
// the add keyboard button
LabelButtonView(
action: { isShowingSheet = true },
label: "Add Keyboard",
systemImage: "plus",
font: .title2
)
.clipShape(.capsule)
.padding([.top, .leading, .trailing])
// binds the visibility state to the sheet builder
.sheet(isPresented: $isShowingSheet) {
InstallKeyboardView()
.frame(width: 960, height: 390)
// FEAT/MAC/CONFIG-WINDOW TODO: Make width and height percentages
}
ScrollView {
VStack {
TabView (selection: $selectedTab) {
VStack {
// the add keyboard button
LabelButtonView(
action: { isShowingSheet = true },
label: "Add Keyboard",
systemImage: "plus",
font: .title2
)
.clipShape(.capsule)
.padding([.top, .leading, .trailing])
// binds the visibility state to the sheet builder
.sheet(isPresented: $isShowingSheet) {
InstallKeyboardView()
.frame(width: 960, height: 390)
// FEAT/MAC/CONFIG-WINDOW TODO: Make width and height percentages
}
Form {
// the view for single keyboard packages
PackageRowView(packages: settings.singleKeyboardPackages, isSingleKeyboardPackage: true, expandedPackageID: $expandedPackageID)
PackageRowView(packages: settings.singleKeyboardPackages, isSingleKeyboardPackage: true, expandedPackageID: $expandedPackageID, showHelpTab: { url in
showHelpTab(for: url)})
// the view for multi keyboard packages
PackageRowView(packages: settings.multiKeyboardPackages, isSingleKeyboardPackage: false, expandedPackageID: $expandedPackageID)
PackageRowView(packages: settings.multiKeyboardPackages, isSingleKeyboardPackage: false, expandedPackageID: $expandedPackageID, showHelpTab: { url in
showHelpTab(for: url) })
}
.padding()
.background(.quinary)
.clipShape(RoundedRectangle(cornerRadius: 12, style: .continuous))
.formStyle(.grouped)
// the Spacer pushes the contents of the VStack to the top of the VStack
Spacer()
}
.padding([.leading, .trailing, .bottom])
.tabItem { Text("Keyboards") }
.tag(0)
if let url = packageSelectedForHelpUrl {
HelpView(helpFileURL: url)
.padding()
.tabItem { Text("Help") }
.tag(1)
} else {
Text("Help not available.")
.font(.title)
.tabItem { Text("Help") }
.tag(1)
}
}
.padding([.leading, .trailing, .bottom])
}
}

View file

@ -39,6 +39,7 @@ public struct PackageInfoView: View {
VStack (alignment: .leading) {
// the text-based package properties presented in a grid
Grid(horizontalSpacing: 10, verticalSpacing: 5) {
// the package version
GridRow {
Text("Package Version:").bold()

View file

@ -20,14 +20,18 @@ public struct PackageRowView: View {
// settings.singleKeyboardPackages or settings.multiKeyboardPackages
let packages: [KeymanPackage]
// a boolean for weather or not a package contains multiple keyboards
let isSingleKeyboardPackage: Bool
// binded to the shared state variable in the parent view
@Binding var expandedPackageID: UUID?
// closure passed from the parent view
let showHelpTab: (URL) -> Void
init(packages: [KeymanPackage], isSingleKeyboardPackage: Bool, expandedPackageID: Binding<UUID?>) {
init(packages: [KeymanPackage], isSingleKeyboardPackage: Bool, expandedPackageID: Binding<UUID?>, showHelpTab: @escaping (URL) -> Void) {
self.packages = packages
self.isSingleKeyboardPackage = isSingleKeyboardPackage
self._expandedPackageID = expandedPackageID
self.showHelpTab = showHelpTab
}
/**
@ -57,6 +61,16 @@ public struct PackageRowView: View {
Text(isSingleKeyboardPackage ? keyboard.name: package.packageName)
.font(.title)
// see keyboard help button
if let url = package.helpFileUrl {
IconButtonView(
action: { showHelpTab(url) },
systemImage: "questionmark.circle",
font: .title2,
helpText: "Show keyboard help"
)
}
// the Spacer pushes the contents of the HStack to the either edge
Spacer()
@ -69,13 +83,7 @@ public struct PackageRowView: View {
.gridColumnAlignment(.leading)
}
// see keyboard help button
IconButtonView(
action: { print("Show keyboard help") },
systemImage: "questionmark.circle",
font: .title2,
helpText: "Show keyboard help"
)
}
// if the package contains multiple keyboards shows an HStack with the keyboard name and toggle button for each keyboard in the package