Skip to content

Commit

Permalink
Implement a basic input method service and keyboard view (#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
WhiredPlanck authored Jan 4, 2025
1 parent 19195bf commit ac72afd
Show file tree
Hide file tree
Showing 7 changed files with 304 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { InputMethodExtensionAbility } from "@kit.IMEKit";
import Want from "@ohos.app.ability.Want";
import keyboardController from "./model/KeyboardController";

export default class FcitxInputMethodService extends InputMethodExtensionAbility {
onCreate(want: Want): void {
keyboardController.onCreate(this.context)
}

onDestroy(): void {
keyboardController.onDestroy()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { inputMethodEngine, InputMethodExtensionContext } from '@kit.IMEKit'
import { display } from '@kit.ArkUI';
import { KeyCode } from '@kit.InputKit';

const ability: inputMethodEngine.InputMethodAbility = inputMethodEngine.getInputMethodAbility();

export class KeyboardController {
private ctx: InputMethodExtensionContext | undefined = undefined;
private panel: inputMethodEngine.Panel | undefined = undefined;
private textInputClient: inputMethodEngine.InputClient | undefined = undefined;
private keyboardController: inputMethodEngine.KeyboardController | undefined = undefined;

constructor() {
}

public onCreate(context: InputMethodExtensionContext): void {
this.ctx = context;
this.initWindow();
this.registerListener();
}

public onDestroy(): void {
this.unRegisterListener();
if (this.panel) {
ability.destroyPanel(this.panel);
}
if (this.ctx) {
this.ctx.destroy();
}
}

public insertText(text: string): void {
if (this.textInputClient) {
this.textInputClient.insertText(text);
}
}

public deleteForward(length: number): void {
if (this.textInputClient) {
this.textInputClient.deleteForward(length);
}
}

private initWindow(): void {
if (this.ctx === undefined) {
return;
}
let dis = display.getDefaultDisplaySync();
let dWidth = dis.width;
let dHeight = dis.height;
let keyHeightRate = 0.3;
let keyHeight = dHeight * keyHeightRate;
let nonBarPosition = dHeight - keyHeight;
let panelInfo: inputMethodEngine.PanelInfo = {
type: inputMethodEngine.PanelType.SOFT_KEYBOARD,
flag: inputMethodEngine.PanelFlag.FLG_FIXED
};
ability.createPanel(this.ctx, panelInfo).then(async (inputPanel: inputMethodEngine.Panel) => {
this.panel = inputPanel;
if (this.panel) {
await this.panel.resize(dWidth, keyHeight);
await this.panel.moveTo(0, nonBarPosition);
await this.panel.setUiContent('InputMethodExtensionAbility/pages/Index');
}
});
}

private registerListener(): void {
this.registerInputListener();
}

private registerInputListener():
void {
ability.on('inputStart', (kbController, textInputClient) => {
this.textInputClient = textInputClient;
this.keyboardController = kbController;
})
ability.on('inputStop', () => {
this.onDestroy();
});
}

private unRegisterListener():
void {
ability.off('inputStart');
ability.off('inputStop', () => {
});
}
}

const keyboardController = new KeyboardController();

export default keyboardController;
130 changes: 130 additions & 0 deletions entry/src/main/ets/InputMethodExtensionAbility/pages/Index.ets
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import { textLayoutData } from './keyboard/LayoutData';
import keyboardController from '../model/KeyboardController';
import { TextKey } from './keyboard/KeyType';

@Component
struct KeyItem {
@State keyValue: TextKey = textLayoutData[0][0];
@State keyBgc: string = "#fff"
@State keyFontColor: string = "#000"

build() {
Column() {
Flex({
direction: FlexDirection.Column,
alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center
}) {
Text(this.keyValue.character).fontSize(20).fontColor(this.keyFontColor)
}
}
.backgroundColor(this.keyBgc)
.borderRadius(6)
.width("8%")
.height("65%")
.onClick(() => {
keyboardController.insertText(this.keyValue.character.toLowerCase());
})
}
}

@Component
export struct BackspaceItem {
@State keyValue: TextKey = { character: '⌫' }
@State keyBgc: string = "#9f9f9f"
@State keyFontColor: string = "#000"

build() {
Column() {
Flex({
direction: FlexDirection.Column,
alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center
}) {
Text(this.keyValue.character).fontSize(20).fontColor(this.keyFontColor)
}
}
.backgroundColor(this.keyBgc)
.width("13%")
.height("65%")
.borderRadius(6)
.onClick(() => {
keyboardController.deleteForward(1);
})
}
}

@Component
export struct SpaceItem {
@State keyValue: TextKey = { character: '␣' }
@State keyBgc: string = "#fff"
@State keyFontColor: string = "#000"

build() {
Column() {
Flex({
direction: FlexDirection.Column,
alignItems: ItemAlign.Center, justifyContent: FlexAlign.Center
}) {
Text(this.keyValue.character).fontSize(20).fontColor(this.keyFontColor)
}
}
.backgroundColor(this.keyBgc)
.width("40%")
.height("65%")
.borderRadius(6)
.onClick(() => {
keyboardController.insertText(' ');
})
}
}

@Component
struct TextKeyboard {
private textList: TextKey[][] = textLayoutData;

build() {
Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center, justifyContent: FlexAlign.SpaceAround }) {
ForEach(this.textList, (row: TextKey[]) => {
Flex({ justifyContent: FlexAlign.SpaceAround }) {
ForEach(row, (column: TextKey) => {
if (column.character == "⌫") {
BackspaceItem();
} else if (column.character == "␣") {
SpaceItem();
} else {
KeyItem({ keyValue: column });
}
}, (column: TextKey) => column.character);
}
.width('96%')
.height('20%')
}, (row: TextKey[]) => textLayoutData.indexOf(row).toString());
}
}
}

@Entry
@Component
struct Index {
build() {
Stack() {
Flex({
direction: FlexDirection.Column,
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.End
}) {
Flex({
direction: FlexDirection.Column,
alignItems: ItemAlign.Center,
justifyContent: FlexAlign.SpaceBetween
}) {
TextKeyboard();
}
.align(Alignment.End)
.width("100%")
.height("96%")
}
.height("100%").align(Alignment.End).backgroundColor("#cdd0d7")
}
.position({ x: 0, y: 0 }).zIndex(99999)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export interface TextKey {
character: string;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { TextKey } from "./KeyType"

export let textLayoutData: TextKey[][] = [
[
{ character: '1' },
{ character: '2' },
{ character: '3' },
{ character: '4' },
{ character: '5' },
{ character: '6' },
{ character: '7' },
{ character: '8' },
{ character: '9' },
{ character: '0' }
],
[
{ character: 'Q' },
{ character: 'W' },
{ character: 'E' },
{ character: 'R' },
{ character: 'T' },
{ character: 'Y' },
{ character: 'U' },
{ character: 'I' },
{ character: 'O' },
{ character: 'P' }
],
[
{ character: 'A' },
{ character: 'S' },
{ character: 'D' },
{ character: 'F' },
{ character: 'G' },
{ character: 'H' },
{ character: 'J' },
{ character: 'K' },
{ character: 'L' },
],
[
{ character: '`' },
{ character: 'Z' },
{ character: 'X' },
{ character: 'C' },
{ character: 'V' },
{ character: 'B' },
{ character: 'N' },
{ character: 'M' },
{ character: '⌫' },
],
[
{ character: ',' },
{ character: '␣' },
{ character: '.' },
]
]
8 changes: 8 additions & 0 deletions entry/src/main/module.json5
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@
"resource": "$profile:backup_config"
}
]
},
{
"description": "inputMethod",
"name": "InputMethodExtensionAbility",
"icon": "$media:app_icon",
"srcEntry": "./ets/InputMethodExtensionAbility/FcitxInputMethodService.ts",
"type": "inputMethod",
"exported": true,
}
]
}
Expand Down
3 changes: 2 additions & 1 deletion entry/src/main/resources/base/profile/main_pages.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
{
"src": [
"pages/Index"
"pages/Index",
"InputMethodExtensionAbility/pages/Index"
]
}

0 comments on commit ac72afd

Please sign in to comment.