diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8289481..19e62f7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,6 +34,7 @@ jobs: - name: Install dependencies run: | brew install \ + nlohmann-json \ extra-cmake-modules IOS_PLATFORM=SIMULATOR ./scripts/install-deps.sh wget https://github.com/fcitx-contrib/fcitx5-macos-prebuilder/releases/download/latest/marisa-$(uname -m).tar.bz2 diff --git a/CMakeLists.txt b/CMakeLists.txt index 11b3570..69ad300 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ option(BUILD_SPELL_DICT "" OFF) find_host_package(PkgConfig) find_host_package(Gettext) +find_host_package(nlohmann_json) add_subdirectory(fcitx5) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 943e631..43b92f4 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,8 +1,11 @@ -add_library(FcitxCommon common.cpp) +add_library(FcitxCommon + common.cpp + inputmethod.cpp +) target_include_directories(FcitxCommon PRIVATE "${PROJECT_SOURCE_DIR}/fcitx5/src/modules/notifications" ) -target_link_libraries(FcitxCommon Fcitx5::Core) +target_link_libraries(FcitxCommon Fcitx5::Core nlohmann_json) add_library(SwiftUtil util.swift) set_target_properties(SwiftUtil PROPERTIES Swift_MODULE_NAME SwiftUtil) diff --git a/common/common-public.h b/common/common-public.h new file mode 100644 index 0000000..9c26d49 --- /dev/null +++ b/common/common-public.h @@ -0,0 +1,3 @@ +#include + +std::string getInputMethods(); diff --git a/common/inputmethod.cpp b/common/inputmethod.cpp new file mode 100644 index 0000000..a0d81ac --- /dev/null +++ b/common/inputmethod.cpp @@ -0,0 +1,35 @@ +#include "common-public.h" +#include "common.h" +#include +#include +#include + +static nlohmann::json jsonDescribeIm(const fcitx::InputMethodEntry *entry) { + nlohmann::json j; + j["name"] = entry->uniqueName(); + j["displayName"] = entry->nativeName() != "" ? entry->nativeName() + : entry->name() != "" ? entry->name() + : entry->uniqueName(); + j["languageCode"] = entry->languageCode(); + return j; +} + +std::string getInputMethods() { + static std::string ret; + nlohmann::json j; + auto &imMgr = instance->inputMethodManager(); + auto group = imMgr.currentGroup(); + bool empty = true; + for (const auto &im : group.inputMethodList()) { + auto entry = imMgr.entry(im.name()); + if (!entry) + continue; + empty = false; + j.push_back(jsonDescribeIm(entry)); + } + if (empty) { // j is not treated array + return "[]"; + } + ret = j.dump(); + return ret.c_str(); +} diff --git a/common/module.modulemap b/common/module.modulemap new file mode 100644 index 0000000..967801a --- /dev/null +++ b/common/module.modulemap @@ -0,0 +1,3 @@ +module FcitxCommon { + header "common-public.h" +} diff --git a/src/App.swift b/src/App.swift index 2f34608..025ae74 100644 --- a/src/App.swift +++ b/src/App.swift @@ -1,8 +1,10 @@ import SwiftUI +import SwiftUtil @main struct Fcitx5App: App { @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate + @Environment(\.scenePhase) private var scenePhase // Dummy. Actual Scene in managed by SceneDelegate. But onOpenURL is // always called on open from URL no matter app is running or not. @@ -10,6 +12,12 @@ struct Fcitx5App: App { WindowGroup { EmptyView().onOpenURL { url in SceneDelegate.contentView?.handleURL(url) + }.onChange(of: scenePhase) { newPhase in + if newPhase == .active { + logger.info("App is active") + sync( + documents.appendingPathComponent("rime"), appGroupData.appendingPathComponent("rime")) + } } } } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ffae501..b2a2855 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -2,6 +2,7 @@ add_executable( ${BUNDLE_NAME} MACOSX_BUNDLE ContentView.swift + ConfigView.swift App.swift AppDelegate.swift SceneDelegate.swift @@ -10,6 +11,7 @@ add_executable( target_include_directories(${BUNDLE_NAME} PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}" + "${PROJECT_SOURCE_DIR}/common" "${PROJECT_BINARY_DIR}/common/$${CMAKE_XCODE_EFFECTIVE_PLATFORMS}" "${PROJECT_BINARY_DIR}/iosnotifications/$${CMAKE_XCODE_EFFECTIVE_PLATFORMS}" "${PROJECT_BINARY_DIR}/deps/AlertToast/$${CMAKE_XCODE_EFFECTIVE_PLATFORMS}" diff --git a/src/ConfigView.swift b/src/ConfigView.swift new file mode 100644 index 0000000..bb0021c --- /dev/null +++ b/src/ConfigView.swift @@ -0,0 +1,12 @@ +import SwiftUI + +struct ConfigView: View { + let inputMethod: InputMethod + + var body: some View { + VStack { + } + .navigationTitle(inputMethod.displayName) + .navigationBarTitleDisplayMode(.inline) + } +} diff --git a/src/ContentView.swift b/src/ContentView.swift index 2f28d18..ffec2e7 100644 --- a/src/ContentView.swift +++ b/src/ContentView.swift @@ -1,15 +1,27 @@ import AlertToast import Fcitx +import FcitxCommon import NotifySwift import SwiftUI import SwiftUtil +struct InputMethod: Codable { + let name: String + let displayName: String + let languageCode: String +} + private class ViewModel: ObservableObject { @Published var url: URL? + @Published var inputMethods = [InputMethod]() + + func refresh() { + inputMethods = try! JSONDecoder().decode( + [InputMethod].self, from: String(getInputMethods()).data(using: .utf8)!) + } } struct ContentView: View { - @Environment(\.scenePhase) private var scenePhase @ObservedObject private var viewModel = ViewModel() // AlertToast fields @@ -25,15 +37,18 @@ struct ContentView: View { } var body: some View { - VStack { - Image(systemName: "globe") - .imageScale(.large) - .foregroundStyle(.tint) - if let url = viewModel.url { - Text("\(url)") + NavigationView { + List { + Section(header: Text("Input Methods")) { + ForEach(viewModel.inputMethods, id: \.name) { inputMethod in + NavigationLink(destination: ConfigView(inputMethod: inputMethod)) { + Text(inputMethod.displayName) + } + } + } } + .navigationTitle("Fcitx5") } - .padding() .toast(isPresenting: $showToast, duration: duration) { AlertToast( displayMode: .alert, @@ -71,11 +86,7 @@ struct ContentView: View { } } }) - } - .onChange(of: scenePhase) { newPhase in - if newPhase == .active { - sync(documents.appendingPathComponent("rime"), appGroupData.appendingPathComponent("rime")) - } + viewModel.refresh() } } }