Skip to content

vChewing/vChewing_InputMethodKit4CSharp

Repository files navigation

vChewing.Utils.MacOS.InputMethodKit

Cross-platform .NET wrapper for Apple's InputMethodKit (IMK). This library provides a managed API to build macOS input methods in C#/.NET. On non-macOS platforms, it compiles as a safe no-op implementation, allowing cross-platform development.

Features

  • IMKServer - Create and manage input method servers
  • IMKCandidates - Display candidate windows with customizable appearance
  • IMKInputController subclassing - Implement input controllers in pure C# via ObjC runtime class registration
  • Cross-platform - Compiles on all platforms; functional on macOS only
  • No external dependencies - Uses P/Invoke to ObjC runtime directly

Requirements

  • .NET 10 SDK (Preview)
  • macOS 10.14+ for runtime functionality

Installation

dotnet add package vChewing.Utils.MacOS.InputMethodKit

Quick Start

Creating an IMKServer

using vChewing.Utils.MacOS.InputMethodKit.Api;

// Create server with bundle identifier
var server = IMKServer.Create("MyInputMethod", "com.example.myim");
if (server.IsValid) {
    Console.WriteLine("IMKServer created successfully");
}

Using IMKCandidates

using vChewing.Utils.MacOS.InputMethodKit.Api;
using vChewing.Utils.MacOS.InputMethodKit.Api.Bindings;

// Create candidate window
var candidates = IMKCandidates.Create(server, IMKCandidatePanelType.SingleColumnScrolling);

// Set candidates
candidates.SetCandidateData(new[] { "候選1", "候選2", "候選3" });

// Configure appearance (macOS 10.14+)
candidates.SetFontSize(16.0);
candidates.WindowLevel = 1000;

// Show/hide
candidates.Show(0);
candidates.Hide();

Implementing a Custom Input Controller

using vChewing.Utils.MacOS.InputMethodKit.Api;

public class MyInputController : InputControllerBase {
    private string _buffer = "";

    public override bool HandleInput(string text, long keyCode, ulong modifiers, IntPtr client) {
        if (string.IsNullOrEmpty(text)) return false;
        
        _buffer += text;
        // Process input...
        return true;
    }

    public override string? GetComposedString(IntPtr client) {
        return _buffer;
    }

    public override void CommitComposition(IntPtr client) {
        _buffer = "";
    }
}

// Register with ObjC runtime (macOS only)
#if MACOS
using vChewing.Utils.MacOS.InputMethodKit.Native.MacOS;

// Native AOT compatible factory-based registration (recommended)
var controllerClassName = InputControllerFactory.RegisterController(
    () => new MyInputController(),
    "MyIMController"
);

// Or use generic version (not AOT compatible, will trigger analyzer warning)
// var controllerClassName = InputControllerFactory.RegisterController<MyInputController>();

var server = IMKServer.CreateWithController("MyIM", controllerClassName);
#endif

API Reference

IMKServer

Method/Property Description
Create(name, bundleId) Create server with bundle identifier
CreateWithController(name, controllerClass) Create server with registered controller class
IsValid Whether the server is valid
BundleIdentifier The bundle identifier
Bundle Native NSBundle pointer
PaletteWillTerminate Palette termination flag
LastKeyEventWasDeadKey Dead key detection

IMKCandidates

Method/Property Description
Create(server, panelType) Create candidate window
Show(locationHint) Show the window
Hide() Hide the window
IsVisible Visibility state
SetCandidateData(string[]) Set candidate strings
GetCandidates() Get current candidates
SelectedCandidate Currently selected index
SelectCandidateWithIdentifier(id) Select by identifier
PanelType Get/set panel type
DismissesAutomatically Auto-dismiss behavior
WindowLevel Window level (10.14+)
SetFontSize(double) Set font size (10.14+)
HandleKeyboardEvent(event) Handle keyboard event (10.14+)

InputControllerBase

Override these methods to implement your input logic:

Method Description
HandleInput(text, keyCode, modifiers, client) Handle keyboard input
HandleEvent(eventPtr, client) Handle raw NSEvent
HandleCommand(selector, client) Handle command selectors
GetComposedString(client) Return composed string
GetCandidates(client) Return candidate array
CommitComposition(client) Commit the composition
ActivateServer(sender) Called on activation
DeactivateServer(sender) Called on deactivation

Project Structure

src/vChewing.Utils.MacOS.InputMethodKit/
├── Api/
│   ├── IMKServer.cs              # Managed IMKServer wrapper
│   ├── IMKCandidates.cs          # Managed IMKCandidates wrapper
│   ├── InputControllerBase.cs    # Base class for input controllers
│   ├── InputMethodKitManager.cs  # Factory and helpers
│   └── Bindings/
│       ├── IMKServerBindings.cs
│       ├── IMKCandidatesBindings.cs
│       ├── IMKInputControllerBindings.cs
│       ├── IMKServerInputBindings.cs
│       ├── IMKTextInputBindings.cs
│       └── ObjCTypes.cs
└── Native/
    ├── MacOS/
    │   ├── MacIMKBridge.cs            # ObjC runtime P/Invoke
    │   ├── MacIMKServer.cs            # macOS IMKServer impl
    │   ├── MacIMKCandidates.cs        # macOS IMKCandidates impl
    │   ├── MacInputMethodKitManager.cs
    │   ├── InputControllerFactory.cs  # ObjC class registration
    │   └── ObjCRuntimeRegistration.cs # Low-level ObjC runtime
    └── Noop/
        └── NoopInputMethodKitManager.cs  # Cross-platform no-op

Building

dotnet build

Testing

dotnet test

Packaging

dotnet pack -c Release src/vChewing.Utils.MacOS.InputMethodKit/vChewing.Utils.MacOS.InputMethodKit.csproj -o ./nupkgs

License

This project is licensed under the MIT License.

About

InputMethodKit support for CSharp with native AOT support. Requiring .NET 10+.

Resources

License

Code of conduct

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages