Skip to content

Commit

Permalink
move about; tested update main program
Browse files Browse the repository at this point in the history
  • Loading branch information
eagleoflqj committed Oct 29, 2024
1 parent 019efbd commit 9d16f89
Show file tree
Hide file tree
Showing 23 changed files with 336 additions and 43 deletions.
11 changes: 11 additions & 0 deletions .vscode/launch.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "attach",
"name": "Debug Fcitx5ConfigTool",
"program": "Fcitx5ConfigTool",
}
]
}
3 changes: 3 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ endif()

set(APP_INSTALL_PATH "/Library/Input Methods")
set(CMAKE_INSTALL_PREFIX "${APP_INSTALL_PATH}/Fcitx5.app/Contents")
set(CONFIGTOOL_INSTALL_PATH "${CMAKE_INSTALL_PREFIX}/MacOS/Fcitx5ConfigTool.app/Contents")
# Reproducible
set(CMAKE_INSTALL_LIBDATADIR "${CMAKE_INSTALL_PREFIX}/lib")

Expand Down Expand Up @@ -100,5 +101,7 @@ set(ADDON_TYPE "StaticLibrary")
add_subdirectory(fcitx5-beast/src)
target_include_directories(beast PRIVATE "${PREBUILT_INSTALL_PATH}/include")

add_subdirectory(config)

enable_testing()
add_subdirectory(tests)
10 changes: 2 additions & 8 deletions assets/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
add_executable(switch_im switch_im.swift)

install(FILES "${CMAKE_CURRENT_SOURCE_DIR}/fcitx.icns"
DESTINATION "${CMAKE_INSTALL_PREFIX}/Resources"
)
Expand All @@ -16,13 +14,9 @@ install(FILES "${CMAKE_CURRENT_BINARY_DIR}/menu_icon.pdf"
DESTINATION "${CMAKE_INSTALL_PREFIX}/Resources"
)

# Preserve execution permission
install(PROGRAMS "${CMAKE_CURRENT_SOURCE_DIR}/uninstall.sh"
"${CMAKE_CURRENT_SOURCE_DIR}/update.sh"
"${CMAKE_CURRENT_BINARY_DIR}/switch_im"
DESTINATION "${CMAKE_INSTALL_PREFIX}/Resources"
)
add_subdirectory(config)

# Preserve execution permission
install(PROGRAMS "${CMAKE_CURRENT_SOURCE_DIR}/fcitx5-curl"
DESTINATION "${CMAKE_INSTALL_PREFIX}/bin"
)
Expand Down
8 changes: 8 additions & 0 deletions assets/config/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
add_executable(switch_im switch_im.swift)

# Preserve execution permission
install(PROGRAMS "${CMAKE_CURRENT_SOURCE_DIR}/uninstall.sh"
"${CMAKE_CURRENT_SOURCE_DIR}/update.sh"
"${CMAKE_CURRENT_BINARY_DIR}/switch_im"
DESTINATION "${CONFIGTOOL_INSTALL_PATH}/Resources"
)
File renamed without changes.
File renamed without changes.
File renamed without changes.
42 changes: 42 additions & 0 deletions config/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
execute_process(COMMAND git rev-parse HEAD
OUTPUT_VARIABLE COMMIT
OUTPUT_STRIP_TRAILING_WHITESPACE
)
execute_process(COMMAND git show --no-patch --format=%ct
OUTPUT_VARIABLE UNIX_TIME
OUTPUT_STRIP_TRAILING_WHITESPACE
)
configure_file(meta.swift.in ${CMAKE_CURRENT_SOURCE_DIR}/meta.swift @ONLY)

file(GLOB CONFIG_SRCS CONFIGURE_DEPENDS *.swift)

add_executable(Fcitx5ConfigTool
MACOSX_BUNDLE
"${PROJECT_SOURCE_DIR}/src/config/officialplugins.swift" # XXX
"${PROJECT_SOURCE_DIR}/src/config/downloader.swift" # XXX
"${PROJECT_SOURCE_DIR}/src/config/util.swift" # XXX
"${PROJECT_SOURCE_DIR}/src/config/installer.swift" # XXX
"${PROJECT_SOURCE_DIR}/src/config/ui.swift" # XXX
${CONFIG_SRCS}
)

set_target_properties(Fcitx5ConfigTool PROPERTIES
MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist"
)

target_link_libraries(Fcitx5ConfigTool
AlertToast
Logging
)

add_custom_command(TARGET Fcitx5ConfigTool POST_BUILD
COMMAND rm -f "${CMAKE_CURRENT_BINARY_DIR}/Fcitx5ConfigTool.app/Contents/MacOS/Fcitx5ConfigTool.d"
)

add_custom_command(TARGET Fcitx5ConfigTool POST_BUILD
COMMAND mkdir -p "${CMAKE_CURRENT_BINARY_DIR}/Fcitx5ConfigTool.app/Contents/Resources" && ln -sf ../../../../Resources/fcitx.icns "${CMAKE_CURRENT_BINARY_DIR}/Fcitx5ConfigTool.app/Contents/Resources/fcitx.icns"
)

install(TARGETS Fcitx5ConfigTool
BUNDLE DESTINATION "${CMAKE_INSTALL_PREFIX}/MacOS"
)
14 changes: 14 additions & 0 deletions config/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?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>CFBundleIdentifier</key>
<string>org.fcitx.Fcitx5Config</string>
<key>CFBundleName</key>
<string>Fcitx5Config</string>
<key>CFBundleDisplayName</key>
<string>Fcitx5</string>
<key>CFBundleIconFile</key>
<string>fcitx.icns</string>
</dict>
</plist>
20 changes: 4 additions & 16 deletions src/config/about.swift → config/about.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,6 @@ func getDate() -> String {
return dateFormatter.string(from: Date(timeIntervalSince1970: TimeInterval(unixTime)))
}

func urlButton(_ text: String, _ link: String) -> some View {
Button(
action: {
if let url = URL(string: link) {
NSWorkspace.shared.open(url)
}
},
label: {
Text(text)
.foregroundColor(.blue)
}
).buttonStyle(PlainButtonStyle())
.focusable(false)
}

let bundleId = "org.fcitx.inputmethod.Fcitx5"
let inputSourceId = bundleId

Expand Down Expand Up @@ -342,7 +327,10 @@ struct AboutView: View {
let path = cacheDir.appendingPathComponent(mainFileName).localPath()
// Necessary to put it in background, otherwise sudo UI will hang if it has been canceled once.
DispatchQueue.global().async {
if !sudo("update", path, updateLog) {
if sudo("update", path, updateLog) {
viewModel.state = .upToDate
showUpToDate = true
} else {
DispatchQueue.main.async {
viewModel.state = .available
showInstallFailed = true
Expand Down
53 changes: 53 additions & 0 deletions config/delegate.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import SwiftUI

let notificationPrefix = "Fcitx5Config"

let aboutNotification = "\(notificationPrefix)About"
let pluginNotification = "\(notificationPrefix)Plugin"

class AppDelegate: NSObject, NSApplicationDelegate {
static var fcitxAbout: FcitxAboutController = {
return FcitxAboutController()
}()

func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {
return true
}

func applicationDidFinishLaunching(_ notification: Notification) {
let center = DistributedNotificationCenter.default()
center.addObserver(
forName: .init(aboutNotification), object: nil, queue: nil
) { _ in
self.showAbout()
}
center.addObserver(
forName: .init(pluginNotification), object: nil, queue: nil
) { _ in
self.showPluginManager()
}
}

func applicationWillTerminate(_ notification: Notification) {
DistributedNotificationCenter.default().removeObserver(self)
}

func showWindow(_ targetWindow: String) {
switch targetWindow {
case "About":
showAbout()
case "Plugin":
showPluginManager()
default:
break
}
}

func showAbout() {
AppDelegate.fcitxAbout.refresh()
AppDelegate.fcitxAbout.showWindow(nil)
}

func showPluginManager() {
}
}
25 changes: 25 additions & 0 deletions config/entry.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import AppKit

@main
struct ConfigApp {
static func main() {
let args = CommandLine.arguments

var targetWindow = args.count > 1 ? args[1] : "About"
if targetWindow != "Plugin" {
targetWindow = "About"
}

let identifier = Bundle.main.bundleIdentifier!
if NSRunningApplication.runningApplications(withBundleIdentifier: identifier).count > 0 {
let center = DistributedNotificationCenter.default()
center.post(name: .init("\(notificationPrefix)\(targetWindow)"), object: nil)
return
}

let delegate = AppDelegate()
NSApplication.shared.delegate = delegate
delegate.showWindow(targetWindow)
NSApplication.shared.run()
}
}
File renamed without changes.
122 changes: 122 additions & 0 deletions config/plugin.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
import AlertToast
import Logging
import SwiftUI
import SwiftyJSON

private let pluginDirectory = libraryDir.appendingPathComponent("plugin")

struct Plugin: Identifiable, Hashable {
let id: String
let category: String
let native: Bool
let github: String?
var dependencies: [String] = []
}

private var pluginMap = officialPlugins.reduce(into: [String: Plugin]()) { result, plugin in
result[plugin.id] = plugin
}

// fcitx5 doesn't unload addons from memory, so once loaded, we have to restart process to use an updated version.
private var inMemoryPlugins: [String] = []
private var needsRestart = false

private func getInstalledPlugins() -> [Plugin] {
let names = getFileNamesWithExtension(pluginDirectory.localPath(), ".json")
return names.map {
pluginMap[$0]
?? Plugin(
id: $0, category: NSLocalizedString("Other", comment: ""), native: true, github: nil)
}
}

private func getFiles(_ descriptor: URL) -> [String] {
guard let json = readJSON(descriptor) else {
FCITX_WARN("Skipped invalid JSON \(descriptor.localPath())")
return []
}
return json["files"].arrayValue.map { $0.stringValue }
}

private func getVersion(_ plugin: String, native: Bool) -> String {
let descriptor = pluginDirectory.appendingPathComponent(plugin + ".json")
guard let json = readJSON(descriptor) else {
return ""
}
return native ? json["version"].stringValue : json["data_version"].stringValue
}

private func getAutoAddIms(_ plugin: String) -> [String] {
let descriptor = pluginDirectory.appendingPathComponent(plugin + ".json")
guard let json = readJSON(descriptor) else {
return []
}
return json["input_methods"].arrayValue.map { $0.stringValue }
}

class PluginVM: ObservableObject {
@Published private(set) var installedPlugins: [Plugin] = []
@Published private(set) var availablePlugins: [Plugin] = []
@Published var upToDate = false

func refreshPlugins() {
installedPlugins = getInstalledPlugins()
availablePlugins.removeAll()
for plugin in officialPlugins {
if !installedPlugins.contains(plugin) {
availablePlugins.append(plugin)
}
}
for plugin in installedPlugins {
if !inMemoryPlugins.contains(plugin.id) {
inMemoryPlugins.append(plugin.id)
}
}
// Allow recheck update on reopen plugin manager.
upToDate = false
}
}

private struct Meta: Codable {
struct Plugin: Codable {
let name: String
// swift-format-ignore: AlwaysUseLowerCamelCase
let data_version: String
let version: String?
}
let plugins: [Plugin]
}

func checkPluginUpdate(_ callback: @escaping (Bool, [String], [String]) -> Void) {
guard let url = URL(string: pluginBaseAddress + "meta-\(arch).json") else {
return callback(false, [], [])
}
URLSession.shared.dataTask(with: url) { data, response, error in
var nativePlugins = [String]()
var dataPlugins = [String]()
if let data = data,
let meta = try? JSONDecoder().decode(Meta.self, from: data)
{
let nativeVersionMap = meta.plugins.reduce(into: [String: String]()) { result, plugin in
result[plugin.name] = plugin.version
}
let dataVersionMap = meta.plugins.reduce(into: [String: String]()) { result, plugin in
result[plugin.name] = plugin.data_version
}
for plugin in getInstalledPlugins() {
if let version = nativeVersionMap[plugin.id], version != getVersion(plugin.id, native: true)
{
nativePlugins.append(plugin.id)
}
if let dataVersion = dataVersionMap[plugin.id],
dataVersion != getVersion(plugin.id, native: false)
{
dataPlugins.append(plugin.id)
}
}
callback(true, nativePlugins, dataPlugins)
} else {
callback(false, [], [])
}
}.resume()
}
File renamed without changes.
21 changes: 21 additions & 0 deletions config/window.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import AppKit

class ConfigWindowController: NSWindowController, NSWindowDelegate {
override init(window: NSWindow?) {
super.init(window: window)
if let window = window {
window.delegate = self
}
}

required init?(coder: NSCoder) {
super.init(coder: coder)
}

override func showWindow(_ sender: Any? = nil) {
if let window = window {
window.makeKeyAndOrderFront(nil)
NSApp.activate(ignoringOtherApps: true)
}
}
}
1 change: 1 addition & 0 deletions scripts/format.sh
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
find macosfrontend macosnotifications webpanel src tests -name '*.cpp' -o -name '*.h' | xargs clang-format -i -style=file:fcitx5/.clang-format
clang-format -i macosfrontend/pasteboard.mm
swift-format format --configuration .swift-format.json --in-place $(find macosfrontend macosnotifications src config assets -name '*.swift')
2 changes: 1 addition & 1 deletion scripts/lint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@ set -e

find macosfrontend macosnotifications webpanel src tests -name '*.cpp' -o -name '*.h' | xargs clang-format -Werror --dry-run -style=file:fcitx5/.clang-format
clang-format -Werror --dry-run macosfrontend/pasteboard.mm
swift-format lint --configuration .swift-format.json -rs macosfrontend macosnotifications src assets
swift-format lint --configuration .swift-format.json -rs macosfrontend macosnotifications src config assets
./scripts/check-code-style.sh
file assets/zh-Hans.lproj/Localizable.strings | grep UTF-16
Loading

0 comments on commit 9d16f89

Please sign in to comment.