• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (c) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import inputMethod from '@ohos.inputMethod';
17import promptAction from '@ohos.promptAction';
18import { logger } from '../utils/Logger';
19import { inputAttribute } from '../utils/InputAttributeInit';
20
21const LINE_HEIGHT: number = 20;
22const END_FLAG: number = 1000;
23const TAG: string = 'CustomInputText';
24
25@Component
26export struct CustomInputText {
27  @State inputText: string = '';
28  @State lastInput: string = '';
29  @State selectInput: string = '';
30  @State cursorInfo: inputMethod.CursorInfo = { top: 0, left: 0, width: 1, height: 25 };
31  @State cursorLeft: number = 0;
32  @State cursorIndex: number = 0;
33  @State selectIndex: number = 0;
34  @State inputWidth: number = 320;
35  @Consume @Watch('isAttachedChange') isAttached: boolean;
36  @Consume @Watch('isOnChange') isOn: boolean;
37  @Consume @Watch('isShowChange') isShow: boolean;
38  @Consume @Watch('changeSelection') isChangeSelection: boolean;
39  @Consume enterKeyIndex: number;
40  @Consume inputTypeIndex: number;
41  @Consume selectStart: number;
42  @Consume selectEnd: number;
43  private inputController: inputMethod.InputMethodController = inputMethod.getController();
44  private settings: RenderingContextSettings = new RenderingContextSettings(true);
45  private context: CanvasRenderingContext2D = new CanvasRenderingContext2D(this.settings);
46
47  build() {
48    Stack() {
49      Row() {
50        Text(this.inputText)
51          .fontSize(16)
52          .fontFamily('sans-serif')
53          .id('inputText')
54          .lineHeight(LINE_HEIGHT)
55          .maxLines(1)
56          .constraintSize({ maxWidth: this.inputWidth })
57        Text(this.selectInput)
58          .fontSize(16)
59          .fontFamily('sans-serif')
60          .lineHeight(LINE_HEIGHT)
61          .id('selectInput')
62          .maxLines(1)
63          .backgroundColor($r('app.color.select_color'))
64        Text(this.lastInput)
65          .fontSize(16)
66          .fontFamily('sans-serif')
67          .lineHeight(LINE_HEIGHT)
68          .id('lastInput')
69          .maxLines(1)
70      }
71      .width(this.inputWidth)
72
73      Text('')
74        .width(this.cursorInfo.width)
75        .height(this.cursorInfo.height)
76        .backgroundColor($r('app.color.cursor_color'))
77        .margin({ top: 5 })
78        .position({ x: this.cursorLeft, y: 0 })
79        .onAreaChange((oldArea: Area, newArea: Area) => {
80          if (newArea.globalPosition.x as number !== this.cursorInfo.left) {
81            this.cursorInfo.left = newArea.globalPosition.x as number;
82            this.cursorInfo.top = newArea.position.y as number;
83            this.cursorInfo.width = newArea.width as number;
84            this.cursorInfo.height = newArea.height as number;
85            logger.info(TAG, `cursor change: this.cursorInfo=${JSON.stringify(this.cursorInfo)}`);
86            this.inputController.updateCursor(this.cursorInfo);
87          }
88        })
89
90      Canvas(this.context)
91        .width('100%')
92        .height(45)
93        .onReady(() => {
94          let px = vp2px(16);
95          this.context.font = px + 'px sans-serif';
96          this.inputWidth = this.context.width;
97        })
98    }
99    .id('customInputText')
100    .width('100%')
101    .borderRadius(20)
102    .backgroundColor($r('app.color.input_text_background'))
103    .padding({ left: 10, right: 10, top: 5, bottom: 5 })
104    .height(45)
105    .onClick((event?: ClickEvent) => {
106      if (event) {
107        logger.info(TAG, `click event= ${JSON.stringify(event)}`);
108        this.initTextInput(event);
109      }
110    })
111  }
112
113  async initTextInput(event: ClickEvent): Promise<void> {
114    focusControl.requestFocus('customInputText');
115    this.inputController.updateAttribute({
116      textInputType: inputAttribute.getInputType(this.inputTypeIndex),
117      enterKeyType: inputAttribute.getEnterType(this.enterKeyIndex)
118    })
119    await this.inputController.attach(false, {
120      inputAttribute: {
121        textInputType: inputAttribute.getInputType(this.inputTypeIndex),
122        enterKeyType: inputAttribute.getEnterType(this.enterKeyIndex)
123      }
124    });
125    this.inputController.showTextInput();
126    this.isAttached = true;
127    this.isShow = true;
128    this.isOn = true;
129    this.calculateCursor(event.x);
130  }
131
132  async isAttachedChange(): Promise<void> {
133    if (this.isAttached) {
134      focusControl.requestFocus('customInputText');
135      await this.inputController.attach(false, {
136        inputAttribute: {
137          textInputType: inputAttribute.getInputType(this.inputTypeIndex),
138          enterKeyType: inputAttribute.getEnterType(this.enterKeyIndex)
139        }
140      });
141    } else {
142      this.detach();
143    }
144  }
145
146  isShowChange(): void {
147    if (this.isShow) {
148      inputMethod.getController().showTextInput();
149    } else {
150      inputMethod.getController().hideTextInput();
151    }
152  }
153
154  isOnChange(): void {
155    if (this.isOn) {
156      this.initListener();
157    } else {
158      this.off();
159    }
160  }
161
162  changeSelection(): void {
163    if (this.isChangeSelection) {
164      let message = this.inputText + this.selectInput + this.lastInput;
165      if (this.selectStart <= this.selectEnd) {
166        this.selectIndex = this.selectStart;
167        this.cursorIndex = this.selectEnd;
168      }
169      if (this.selectStart > this.selectEnd) {
170        this.selectIndex = this.selectEnd;
171        this.cursorIndex = this.selectStart;
172      }
173      if (this.cursorIndex > message.length) {
174        this.cursorIndex = message.length;
175      }
176      this.inputText = message.substring(0, this.selectIndex);
177      this.selectInput = message.substring(this.selectIndex, this.cursorIndex);
178      this.lastInput = message.substring(this.cursorIndex, message.length);
179      let cursorText = this.inputText + this.selectInput;
180      this.cursorLeft = this.context.measureText(cursorText).width;
181      this.isChangeSelection = false;
182    }
183  }
184
185  async detach(): Promise<void> {
186    logger.info(TAG, `detach`);
187    await this.off();
188    this.isOn = false;
189    this.isShow = false;
190    this.inputController.detach();
191  }
192
193  async off(): Promise<void> {
194    logger.info(TAG, `off`);
195    this.inputController.off('insertText');
196    this.inputController.off('deleteLeft');
197    this.inputController.off('deleteRight');
198    this.inputController.off('moveCursor');
199    this.inputController.off('selectByMovement');
200    this.inputController.off('selectByRange');
201    this.inputController.off('sendFunctionKey')
202    this.inputController.off('handleExtendAction');
203    this.inputController.off('sendKeyboardStatus');
204  }
205
206  initListener(): void {
207    this.inputController.on('insertText', (text: string) => {
208      logger.info(TAG, `insertText, text: ${text}`);
209      if ((this.cursorLeft + this.context.measureText(text).width + this.context.measureText(this.lastInput)
210        .width) > this.context.width) {
211        return;
212      }
213      this.inputText += text;
214      this.cursorIndex = this.inputText.length;
215      this.selectIndex = this.cursorIndex;
216      this.selectInput = '';
217      this.cursorLeft = this.context.measureText(this.inputText).width;
218    })
219    this.inputController.on('deleteRight', (length: number) => {
220      let message = this.inputText + this.selectInput + this.lastInput;
221      if (this.cursorIndex < message.length) {
222        this.selectIndex = this.cursorIndex;
223        this.selectInput = '';
224        let deleteIndex = this.cursorIndex + length;
225        if (deleteIndex > message.length) {
226          deleteIndex = message.length;
227        }
228        this.lastInput = message.substring(this.cursorIndex + length, message.length);
229      }
230    })
231    this.inputController.on('deleteLeft', (length: number) => {
232      this.inputText = this.inputText.substring(0, this.inputText.length - length);
233      this.cursorIndex = this.inputText.length;
234      this.selectIndex = this.cursorIndex;
235      this.cursorLeft = this.context.measureText(this.inputText).width;
236    })
237    this.inputController.on('moveCursor', (direction: inputMethod.Direction) => {
238      logger.info(TAG, `Succeeded in moveCursor, direction: ${direction}`);
239      let message = this.inputText + this.selectInput + this.lastInput;
240      this.selectInput = '';
241      if (direction === inputMethod.Direction.CURSOR_UP) {
242        this.cursorIndex = 0;
243      }
244      if (direction === inputMethod.Direction.CURSOR_DOWN) {
245        this.cursorIndex = message.length;
246      }
247      if (direction === inputMethod.Direction.CURSOR_LEFT) {
248        this.cursorIndex--;
249      }
250      if (direction === inputMethod.Direction.CURSOR_RIGHT) {
251        if (this.cursorIndex < message.length) {
252          this.cursorIndex++;
253        }
254      }
255      this.selectIndex = this.cursorIndex;
256      this.inputText = message.substring(0, this.cursorIndex);
257      this.lastInput = message.substring(this.cursorIndex, message.length);
258      this.cursorLeft = this.context.measureText(this.inputText).width;
259    });
260    this.inputController.on('selectByMovement', (movement: inputMethod.Movement) => {
261      logger.info(TAG, `Succeeded in selectByMovement, direction: ${movement.direction}`);
262      let message = this.inputText + this.selectInput + this.lastInput;
263      if (movement.direction === inputMethod.Direction.CURSOR_UP) {
264        this.selectIndex = 0;
265      }
266      if (movement.direction === inputMethod.Direction.CURSOR_LEFT) {
267        if (this.selectIndex > 0) {
268          this.selectIndex--;
269        }
270      }
271      if (movement.direction === inputMethod.Direction.CURSOR_RIGHT) {
272        if (this.selectIndex < message.length) {
273          this.selectIndex++;
274        }
275      }
276      if (movement.direction === inputMethod.Direction.CURSOR_DOWN) {
277        this.selectIndex = message.length;
278      }
279      if (this.selectIndex > this.cursorIndex) {
280        this.inputText = message.substring(0, this.cursorIndex);
281        this.selectInput = message.substring(this.cursorIndex, this.selectIndex);
282        this.lastInput = message.substring(this.selectIndex, message.length);
283      } else {
284        this.inputText = message.substring(0, this.selectIndex);
285        this.selectInput = message.substring(this.selectIndex, this.cursorIndex);
286        this.lastInput = message.substring(this.cursorIndex, message.length);
287      }
288    });
289    this.inputController.on('selectByRange', (range: inputMethod.Range) => {
290      logger.info(TAG, `selectByRange this.range: ${JSON.stringify(range)}`);
291      let message = this.inputText + this.selectInput + this.lastInput;
292      if (range.start === 0 && range.end === 0) {
293        this.cursorIndex = 0;
294        let message = this.inputText + this.selectInput + this.lastInput;
295        this.selectInput = '';
296        this.selectIndex = this.cursorIndex;
297        this.inputText = message.substring(0, this.cursorIndex);
298        this.lastInput = message.substring(this.cursorIndex, message.length);
299        this.cursorLeft = this.context.measureText(this.inputText).width;
300      } else if (range.end > range.start) {
301        if (range.end === END_FLAG) {
302          this.lastInput = '';
303          this.selectIndex = message.length;
304          this.inputText = message.substring(0, this.cursorIndex);
305          this.selectInput = message.substring(this.cursorIndex, this.selectIndex);
306        } else {
307          this.selectIndex = 0;
308          this.inputText = ''
309          this.selectInput = message.substring(0, this.cursorIndex);
310          this.lastInput = message.substring(this.cursorIndex, message.length);
311        }
312      } else {
313        this.cursorIndex = message.length;
314        this.selectIndex = this.cursorIndex;
315        this.inputText = message.substring(0, this.cursorIndex);
316        this.lastInput = message.substring(this.cursorIndex, message.length);
317        this.cursorLeft = this.context.measureText(this.inputText).width;
318      }
319      logger.info(TAG, `selectByRange this.selectInput: ${this.selectInput}`);
320    })
321    this.inputController.on('sendFunctionKey', (enterKey: inputMethod.FunctionKey) => {
322      promptAction.showToast({ message: `enterKey Clicked ${enterKey.enterKeyType.toString()}`, bottom: 500 });
323    })
324    this.inputController.on('sendKeyboardStatus', (keyBoardStatus: inputMethod.KeyboardStatus) => {
325      logger.info(TAG, `sendKeyboardStatus keyBoardStatus: ${keyBoardStatus}`);
326    });
327    this.inputController.on('handleExtendAction', (action: inputMethod.ExtendAction) => {
328      if (action === inputMethod.ExtendAction.SELECT_ALL) {
329        let message = this.inputText + this.selectInput + this.lastInput;
330        this.cursorIndex = message.length;
331        this.selectIndex = 0;
332        this.inputText = ''
333        this.selectInput = message.substring(0, this.cursorIndex);
334        this.lastInput = '';
335        this.cursorLeft = this.context.measureText(this.selectInput).width;
336      }
337    })
338  }
339
340  calculateCursor(x: number): void {
341    let message = this.inputText + this.selectInput + this.lastInput;
342    let charWidth = this.context.measureText(message).width / message.length;
343    this.cursorIndex = Math.floor(x / charWidth);
344    if (this.cursorIndex < 0) {
345      this.cursorIndex = 0;
346      this.inputText = '';
347      this.lastInput = message;
348    } else if (this.cursorIndex > message.length) {
349      this.cursorIndex = message.length;
350      this.inputText = message;
351      this.lastInput = '';
352    } else {
353      this.inputText = message.substring(0, this.cursorIndex);
354      this.lastInput = message.substring(this.cursorIndex, message.length);
355    }
356    this.selectIndex = this.cursorIndex;
357    this.selectInput = '';
358    this.cursorLeft = this.context.measureText(message.substring(0, this.cursorIndex)).width;
359  }
360}