From 2821fce2e3c3cf2278285fd8ee5272d3c859d878 Mon Sep 17 00:00:00 2001 From: ienaga Date: Sun, 24 Nov 2024 11:17:08 +0900 Subject: [PATCH] =?UTF-8?q?#154=20TextField=E3=81=AE=E3=82=BF=E3=83=83?= =?UTF-8?q?=E3=83=97=E5=87=A6=E7=90=86=E3=82=92=E5=AE=9F=E8=A3=85(WIP)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../usecase/CreateRootMovieClipUseCase.ts | 8 +- .../core/src/Next2D/usecase/LoadUseCase.ts | 6 +- packages/core/src/Player.ts | 572 +----------------- ...rvice.ts => PlayerAppendElementService.ts} | 6 +- .../service/PlayerKeyDownEventService.ts | 7 + .../Player/service/PlayerKeyUpEventService.ts | 6 + .../service/PlayerPointerDownEventService.ts | 32 +- .../src/Player/usecase/PlayerBootUseCase.ts | 40 ++ .../usecase/TextAreaRegisterEventUseCase.ts | 4 - packages/text/src/TextField.ts | 56 +- .../usecase/TextFieldArrowDownUseCase.ts | 68 ++- .../usecase/TextFieldArrowLeftUseCase.ts | 18 +- .../usecase/TextFieldArrowRightUseCase.ts | 18 +- .../usecase/TextFieldArrowUpUseCase.ts | 56 +- .../usecase/TextFieldCopyUseCase.test.ts | 14 + .../usecase/TextFieldInsertTextUseCase.ts | 6 +- .../usecase/TextFieldKeyDownEventUseCase.ts} | 39 +- .../usecase/TextFieldSelectAllUseCase.ts | 24 + .../usecase/TextFieldSetFocusUseCase.ts | 6 +- packages/text/src/TextUtil.ts | 4 +- 20 files changed, 348 insertions(+), 642 deletions(-) rename packages/core/src/Player/service/{PlayerAppendCanvasElementService.ts => PlayerAppendElementService.ts} (59%) create mode 100644 packages/core/src/Player/usecase/PlayerBootUseCase.ts rename packages/text/src/{TextArea/usecase/TextAreaKeydownUseCase.ts => TextField/usecase/TextFieldKeyDownEventUseCase.ts} (59%) create mode 100644 packages/text/src/TextField/usecase/TextFieldSelectAllUseCase.ts diff --git a/packages/core/src/Next2D/usecase/CreateRootMovieClipUseCase.ts b/packages/core/src/Next2D/usecase/CreateRootMovieClipUseCase.ts index 43552397..b23379bc 100644 --- a/packages/core/src/Next2D/usecase/CreateRootMovieClipUseCase.ts +++ b/packages/core/src/Next2D/usecase/CreateRootMovieClipUseCase.ts @@ -1,9 +1,9 @@ import type { IPlayerOptions } from "../../interface/IPlayerOptions"; -import { $player } from "../../Player"; import { $clamp } from "../../CoreUtil"; import { execute as playerRemoveLoadingElementService } from "../../Player/service/PlayerRemoveLoadingElementService"; -import { execute as playerAppendCanvasElementService } from "../../Player/service/PlayerAppendCanvasElementService"; +import { execute as playerAppendElementService } from "../../Player/service/PlayerAppendElementService"; import { execute as playerReadyCompleteUseCase } from "../../Player/usecase/PlayerReadyCompleteUseCase"; +import { execute as playerBootUseCase } from "../../Player/usecase/PlayerBootUseCase"; import { Sprite, $stage @@ -37,7 +37,7 @@ export const execute = async ( } // boot player - $player.boot(options); + playerBootUseCase(options); const root = $stage.addChild(new Sprite()); @@ -48,7 +48,7 @@ export const execute = async ( playerRemoveLoadingElementService(); // append canvas - playerAppendCanvasElementService(); + playerAppendElementService(); return root; }; \ No newline at end of file diff --git a/packages/core/src/Next2D/usecase/LoadUseCase.ts b/packages/core/src/Next2D/usecase/LoadUseCase.ts index 94b37cb0..ef389ff7 100644 --- a/packages/core/src/Next2D/usecase/LoadUseCase.ts +++ b/packages/core/src/Next2D/usecase/LoadUseCase.ts @@ -1,14 +1,14 @@ import type { IPlayerOptions } from "../../interface/IPlayerOptions"; import type { IStageData } from "../../interface/IStageData"; import type { MovieClip } from "@next2d/display"; -import { $player } from "../../Player"; import { $clamp } from "../../CoreUtil"; import { URLRequest } from "@next2d/net"; import { IOErrorEvent } from "@next2d/events"; import { execute as playerResizeEventUseCase } from "../../Player/usecase/PlayerResizeEventUseCase"; import { execute as playerRemoveLoadingElementService } from "../../Player/service/PlayerRemoveLoadingElementService"; -import { execute as playerAppendCanvasElementService } from "../../Player/service/PlayerAppendCanvasElementService"; +import { execute as playerAppendCanvasElementService } from "../../Player/service/PlayerAppendElementService"; import { execute as playerReadyCompleteUseCase } from "../../Player/usecase/PlayerReadyCompleteUseCase"; +import { execute as playerBootUseCase } from "../../Player/usecase/PlayerBootUseCase"; import { Loader, $stage @@ -47,7 +47,7 @@ export const execute = async (url: string, options: IPlayerOptions | null = null } // player - $player.boot(options); + playerBootUseCase(options); const loader: Loader = new Loader(); diff --git a/packages/core/src/Player.ts b/packages/core/src/Player.ts index 07ac4bf6..3f4dadfa 100644 --- a/packages/core/src/Player.ts +++ b/packages/core/src/Player.ts @@ -1,11 +1,7 @@ import type { IPlayerOptions } from "./interface/IPlayerOptions"; import { $cacheStore } from "@next2d/cache"; import { $rendererWorker } from "./RendererWorker"; -import { execute as playerCreateContainerElementService } from "./Player/service/PlayerCreateContainerElementService"; -import { execute as playerApplyContainerElementStyleService } from "./Player/service/PlayerApplyContainerElementStyleService"; -import { execute as playerLoadingAnimationService } from "./Player/service/PlayerLoadingAnimationService"; import { execute as playerResizeEventService } from "./Player/usecase/PlayerResizeEventUseCase"; -import { execute as playerResizeRegisterService } from "./Player/usecase/PlayerResizeRegisterUseCase"; import { execute as playerPlayUseCase } from "./Player/usecase/PlayerPlayUseCase"; import { execute as playerStopService } from "./Player/service/PlayerStopService"; import { execute as playerRegisterEventUseCase } from "./Player/usecase/PlayerRegisterEventUseCase"; @@ -75,9 +71,9 @@ export class Player * * @type {number} * @default 0 - * @private + * @public */ - private _$fixedWidth: number; + public fixedWidth: number; /** * @description optionで指定された描画領域の固定高さ、optionで指定されない場合は0 @@ -85,9 +81,9 @@ export class Player * * @type {number} * @default 0 - * @private + * @public */ - private _$fixedHeight: number; + public fixedHeight: number; /** * @description Playerの停止フラグ @@ -159,17 +155,6 @@ export class Player */ public mouseState: "up" | "down"; - // private _$textField: TextField | null; - // private _$rollOverObject: EventDispatcherImpl | null; - // private _$mouseOverTarget: EventDispatcherImpl | null; - // private _$hitTestStart: boolean; - // private _$stageX: number; - // private _$stageY: number; - // private _$deltaX: number; - // private _$deltaY: number; - // private _$clickTarget: ParentImpl | null; - // private _$actionProcess: boolean; - /** * @constructor * @public @@ -189,59 +174,10 @@ export class Player this.mouseState = "up"; // options - this._$fixedWidth = 0; - this._$fixedHeight = 0; - this._$tagId = ""; - this._$fullScreen = false; - - // /** - // * @type {DisplayObject} - // * @default null - // * @private - // */ - // this._$rollOverObject = null; - - // /** - // * @type {DisplayObject} - // * @default null - // * @private - // */ - // this._$mouseOverTarget = null; - - // /** - // * @type {string} - // * @default up - // * @private - // */ - // this._$state = "up"; - - // /** - // * @type {boolean} - // * @default false - // * @private - // */ - // this._$hitTestStart = false; - - // /** - // * @type {TextField} - // * @default null - // * @private - // */ - // this._$textField = null; - - // /** - // * @type {DisplayObject} - // * @default null - // * @private - // */ - // this._$clickTarget = null; - - // /** - // * @type {boolean} - // * @default false - // * @private - // */ - // this._$actionProcess = false; + this.fixedWidth = 0; + this.fixedHeight = 0; + this._$tagId = ""; + this._$fullScreen = false; playerRegisterEventUseCase(); } @@ -327,495 +263,11 @@ export class Player return ; } - this._$fixedWidth = options.width || this._$fixedWidth; - this._$fixedHeight = options.height || this._$fixedHeight; - this._$tagId = options.tagId || this._$tagId; - this._$fullScreen = !!options.fullScreen; + this.fixedWidth = options.width || this.fixedWidth; + this.fixedHeight = options.height || this.fixedHeight; + this._$tagId = options.tagId || this._$tagId; + this._$fullScreen = !!options.fullScreen; } - - /** - * @description Playerの初期起動処理 - * Initial startup processing of Player - * - * @return {void} - * @method - * @public - */ - boot (options: IPlayerOptions | null = null): void - { - this.setOptions(options); - - // create element - const element = playerCreateContainerElementService(); - - // apply base style - playerApplyContainerElementStyleService( - element, this._$fixedWidth, this._$fixedHeight - ); - - // start loading - playerLoadingAnimationService(element); - - // register resize event - if (!this._$fixedWidth && !this._$fixedHeight) { - playerResizeRegisterService(); - } - - // initialize resize - playerResizeEventService(); - } - - // /** - // * @param {number} timestamp - // * @return {void} - // * @method - // * @private - // */ - // async _$run (timestamp: number = 0): Promise - // { - // if (this._$stopFlag) { - // return ; - // } - - // // delay action - // this._$doAction(); - - // const delta: number = timestamp - this._$startTime; - // if (delta > this._$fps) { - - // // update - // this._$startTime = timestamp - delta % this._$fps; - - // // execute - // this._$action(); - - // // start sound - // if (this._$sounds.size) { - // for (const movieClip of this._$sounds.values()) { - // movieClip._$soundPlay(); - // } - // this._$sounds.clear(); - // } - - // // draw - // this._$draw(); - - // // draw event - // if (!$isTouch - // && !this._$hitTestStart - // && this._$state === "up" - // && this._$stageX > -1 - // && this._$stageY > -1 - // && $getEvent() - // ) { - // this._$pointerCheck(); - // } - - // } - - // // next frame - // this._$timerId = requestAnimationFrame((timestamp: number) => - // { - // this._$run(timestamp); - // }); - // } - - // /** - // * @return {void} - // * @method - // * @private - // */ - // _$pointerCheck (): void - // { - // const stageX: number = this._$stageX; - // const stageY: number = this._$stageY; - - // // setup - // this._$hitObject.x = stageX; - // this._$hitObject.y = stageY; - // this._$hitObject.pointer = ""; - // this._$hitObject.hit = null; - - // // reset - // $hitContext.setTransform(1, 0, 0, 1, 0, 0); - // $hitContext.beginPath(); - - // // hit test - // $MATRIX_HIT_ARRAY_IDENTITY[4] = this.tx; - // $MATRIX_HIT_ARRAY_IDENTITY[5] = this.ty; - // this._$stage._$mouseHit( - // $hitContext, $MATRIX_HIT_ARRAY_IDENTITY, - // this._$hitObject, true - // ); - - // // change state - // // params - // let instance: DisplayObjectImpl = null; - // let target: DisplayObjectImpl = null; - // let canPointerText: boolean = false; - // let canPointer: boolean = false; - - // // execute - // if (this._$hitObject.hit) { - - // instance = this._$hitObject.hit; - - // // (1) mouseOut - // if (this._$mouseOverTarget - // && this._$mouseOverTarget !== instance - // ) { - - // const outInstance: DisplayObjectImpl = this._$mouseOverTarget; - - // if (outInstance.willTrigger(Next2DMouseEvent.MOUSE_OUT)) { - // outInstance.dispatchEvent(new Next2DMouseEvent( - // Next2DMouseEvent.MOUSE_OUT, true, false - // )); - // } - - // } - - // // rollOut and rollOver - // if (this._$rollOverObject !== instance) { - - // let hitParent = null; - // if (this._$rollOverObject) { - - // // (2) prev object rollOut - // target = this._$rollOverObject; - - // if (target.willTrigger(Next2DMouseEvent.ROLL_OUT)) { - // target.dispatchEvent(new Next2DMouseEvent( - // Next2DMouseEvent.ROLL_OUT, false, false - // )); - // } - - // // rollOver flag instance - // hitParent = target._$parent; - // while (hitParent && hitParent._$root !== hitParent) { - - // if (hitParent === instance) { - // break; - // } - - // if (hitParent._$mouseEnabled - // && hitParent._$outCheck(stageX, stageY) - // ) { - - // let isUpperLayer = false; - // let check = instance; - // while (check && check._$root !== check) { - - // if (check !== hitParent) { - // check = check._$parent; - // continue; - // } - - // isUpperLayer = true; - - // break; - // } - - // if (!isUpperLayer && hitParent._$parent === instance._$parent - // && hitParent._$index > instance._$index - // ) { - // isUpperLayer = true; - // } - - // if (isUpperLayer) { - // break; - // } - - // } - - // if (hitParent.willTrigger(Next2DMouseEvent.ROLL_OUT)) { - // hitParent.dispatchEvent(new Next2DMouseEvent( - // Next2DMouseEvent.ROLL_OUT, false, false - // )); - // } - - // hitParent = hitParent._$parent; - - // } - // } - - // // (3) current object rollOver - // target = instance; - // for (;;) { - - // if (target.willTrigger(Next2DMouseEvent.ROLL_OVER)) { - // target.dispatchEvent(new Next2DMouseEvent( - // Next2DMouseEvent.ROLL_OVER, false, false - // )); - // } - - // target = target._$parent; - // if (!target || target === hitParent - // || target.stage === target - // ) { - // break; - // } - - // } - - // } - - // this._$rollOverObject = instance; - - // // (4) mouseOver - // switch (true) { - - // case this._$mouseOverTarget === null: - // case this._$mouseOverTarget !== instance: - - // if (instance && instance.willTrigger(Next2DMouseEvent.MOUSE_OVER)) { - // instance.dispatchEvent(new Next2DMouseEvent( - // Next2DMouseEvent.MOUSE_OVER, true, false - // )); - // } - - // // set target - // this._$mouseOverTarget = instance; - // break; - - // } - - // // click reset - // if (this._$state === "up") { - // this._$clickTarget = null; - // } - - // // PC - // if (!$isTouch && this._$state === "up") { - - // target = instance; - // while (target && target.root !== target) { - - // if ("_$text" in target) { - // if (target.type === "input") { - // canPointerText = true; - // break; - // } - // } - - // if ("buttonMode" in target && target.buttonMode) { - // canPointer = true; - // break; - // } - - // target = target._$parent; - - // } - - // } - - // } else { - - // // (1) mouseOut - // if (this._$mouseOverTarget) { - - // instance = this._$mouseOverTarget; - - // if (instance.willTrigger(Next2DMouseEvent.MOUSE_OUT)) { - // instance.dispatchEvent(new Next2DMouseEvent( - // Next2DMouseEvent.MOUSE_OUT, true, false - // )); - // } - // } - - // // (2) rollOut - // if (this._$rollOverObject) { - - // target = this._$rollOverObject; - - // // parent target - // while (target && target.root !== target) { - - // if (target.willTrigger(Next2DMouseEvent.ROLL_OUT)) { - // target.dispatchEvent(new Next2DMouseEvent( - // Next2DMouseEvent.ROLL_OUT, false, false - // )); - // } - - // target = target._$parent; - - // } - - // } - - // // reset - // this._$rollOverObject = null; - // this._$mouseOverTarget = null; - // } - - // // change cursor - // switch (true) { - - // case canPointerText: - // this._$canvas.style.cursor = "text"; - // break; - - // case canPointer: - // this._$canvas.style.cursor = "pointer"; - // break; - - // case !$isTouch && this._$state === "up": - // this._$canvas.style.cursor = "auto"; - // break; - - // } - - // if (this._$actions.length > 1) { - // this._$doAction(); - // } - // } - - // /** - // * @return {void} - // * @method - // * @private - // */ - // _$action (): void - // { - - // if (this._$stopFlag) { - // return ; - // } - - // let loaders = null; - - // const length = this._$loaders.length; - // if (length) { - - // // clone - // loaders = this._$loaders.slice(0); - - // // array reset - // this._$loaders.length = 0; - - // for (let idx = 0; idx < length; ++idx) { - - // const loader = loaders[idx]; - - // // first action - // if ("content" in loader) { - // loader.content._$prepareActions(); - // } - // } - // } - - // // next frame - // this._$stage._$nextFrame(); - - // // enter frame event - // if (this._$broadcastEvents.has(Next2DEvent.ENTER_FRAME)) { - // this._$dispatchEvent(new Next2DEvent(Next2DEvent.ENTER_FRAME)); - // } - - // // constructed event - // if (this._$broadcastEvents.has(Next2DEvent.FRAME_CONSTRUCTED)) { - // this._$dispatchEvent(new Next2DEvent(Next2DEvent.FRAME_CONSTRUCTED)); - // } - - // // execute frame action - // this._$doAction(); - - // // exit event - // if (this._$broadcastEvents.has(Next2DEvent.EXIT_FRAME)) { - // this._$dispatchEvent(new Next2DEvent(Next2DEvent.EXIT_FRAME)); - // } - - // // render event - // if (this._$stage._$invalidate) { - - // // reset - // this._$stage._$invalidate = false; - - // // execute render event - // this._$dispatchEvent(new Next2DEvent(Next2DEvent.RENDER)); - - // } - - // // loader events - // if (loaders) { - - // for (let idx: number = 0; idx < loaders.length; ++idx) { - - // const loader = loaders[idx]; - - // // init event - // if (loader.hasEventListener(Next2DEvent.INIT)) { - // loader.dispatchEvent(new Next2DEvent(Next2DEvent.INIT)); - // } - - // // complete event - // if (loader.hasEventListener(Next2DEvent.COMPLETE)) { - // loader.dispatchEvent(new Next2DEvent(Next2DEvent.COMPLETE)); - // } - - // } - - // // pool - // $poolArray(loaders); - // } - - // // execute frame action - // this._$doAction(); - // } - - // /** - // * @return {void} - // * @method - // * @private - // */ - // _$doAction (): void - // { - // while (this._$actions.length) { - - // this._$actionProcess = true; - - // // target object - // const mc: MovieClip | void = this._$actions.pop(); - // if (!mc) { - // continue; - // } - - // mc._$canAction = false; - // mc._$actionOffset = 0; - // mc._$actionLimit = 0; - - // const frame: number = mc._$currentFrame; - // if (!mc._$actions.has(frame)) { - // continue; - // } - - // const actions: Function[] | void = mc._$actions.get(frame); - // if (!actions) { - // continue; - // } - - // mc._$actionProcess = true; - // for (let idx: number = 0; idx < actions.length; ++idx) { - // $setCurrentLoaderInfo(mc._$loaderInfo); - // actions[idx].apply(mc); - // } - // mc._$actionProcess = false; - - // // adjustment - // if (mc._$frameCache.size) { - // mc._$currentFrame = mc._$frameCache.get("nextFrame"); - // mc._$clearChildren(); - - // mc._$stopFlag = mc._$frameCache.get("stopFlag"); - // mc._$isPlaying = mc._$frameCache.get("isPlaying"); - // mc._$frameCache.clear(); - // } - - // } - - // this._$actionProcess = false; - // $setCurrentLoaderInfo(null); - // } } export const $player = new Player(); \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerAppendCanvasElementService.ts b/packages/core/src/Player/service/PlayerAppendElementService.ts similarity index 59% rename from packages/core/src/Player/service/PlayerAppendCanvasElementService.ts rename to packages/core/src/Player/service/PlayerAppendElementService.ts index 4e582a03..0ec74ded 100644 --- a/packages/core/src/Player/service/PlayerAppendCanvasElementService.ts +++ b/packages/core/src/Player/service/PlayerAppendElementService.ts @@ -1,9 +1,10 @@ import { $getMainElement } from "../../CoreUtil"; import { $canvas } from "../../Canvas"; +import { $textArea } from "@next2d/text"; /** - * @description canvas elementをメインのdivに追加 - * Add canvas element to main div + * @description canvas と textarea elementをメインのdivに追加 + * Add canvas and textarea element to main div. * * @return {void} * @method @@ -16,4 +17,5 @@ export const execute = (): void => return ; } element.appendChild($canvas); + element.appendChild($textArea); }; \ No newline at end of file diff --git a/packages/core/src/Player/service/PlayerKeyDownEventService.ts b/packages/core/src/Player/service/PlayerKeyDownEventService.ts index 2ee13d1f..8f8cf9dd 100644 --- a/packages/core/src/Player/service/PlayerKeyDownEventService.ts +++ b/packages/core/src/Player/service/PlayerKeyDownEventService.ts @@ -1,4 +1,5 @@ import { $stage } from "@next2d/display"; +import { $getSelectedTextField } from "@next2d/text"; import { KeyboardEvent as Event, $setEvent @@ -15,6 +16,12 @@ import { */ export const execute = (event: KeyboardEvent): void => { + const selectedTextField = $getSelectedTextField(); + if (selectedTextField) { + selectedTextField.keyDown(event); + return ; + } + if (!$stage.hasEventListener(Event.KEY_DOWN)) { return ; } diff --git a/packages/core/src/Player/service/PlayerKeyUpEventService.ts b/packages/core/src/Player/service/PlayerKeyUpEventService.ts index 5e711701..963e8e71 100644 --- a/packages/core/src/Player/service/PlayerKeyUpEventService.ts +++ b/packages/core/src/Player/service/PlayerKeyUpEventService.ts @@ -1,4 +1,5 @@ import { $stage } from "@next2d/display"; +import { $getSelectedTextField } from "@next2d/text"; import { KeyboardEvent as Event, $setEvent @@ -15,6 +16,11 @@ import { */ export const execute = (event: KeyboardEvent): void => { + const selectedTextField = $getSelectedTextField(); + if (selectedTextField) { + return ; + } + if (!$stage.hasEventListener(Event.KEY_UP)) { return ; } diff --git a/packages/core/src/Player/service/PlayerPointerDownEventService.ts b/packages/core/src/Player/service/PlayerPointerDownEventService.ts index 44709aa9..0a780701 100644 --- a/packages/core/src/Player/service/PlayerPointerDownEventService.ts +++ b/packages/core/src/Player/service/PlayerPointerDownEventService.ts @@ -27,19 +27,21 @@ export const execute = ( page_y: number = 0 ): void => { - const displayObject = $hitObject.hit as D; + const displayObject = $hitObject.hit as unknown as D; + + // 選択中のTextFieldがある場合はフォーカスを解除します。 + const selectedTextField = $getSelectedTextField(); + if (selectedTextField) { + if (!displayObject || selectedTextField.instanceId !== displayObject.instanceId) { + selectedTextField.focus = false; + $setSelectedTextField(null); + } + } + if (displayObject) { if (displayObject.isText) { - // 選択中のTextFieldがある場合はフォーカスを解除します。 - const selectedTextField = $getSelectedTextField(); - if (selectedTextField - && selectedTextField.instanceId !== displayObject.instanceId - ) { - selectedTextField.focus = false; - } - if (!(displayObject as unknown as TextField).focus) { (displayObject as unknown as TextField).focus = true; $setSelectedTextField(displayObject as unknown as TextField); @@ -50,8 +52,8 @@ export const execute = ( $hitObject.y - $hitMatrix[5] ); - $textArea.style.top = `${page_x}px`; - $textArea.style.left = `${page_y}px`; + $textArea.style.left = `${page_x}px`; + $textArea.style.top = `${page_y}px`; } // ヒットしたDisplayObjectポインターダウンイベントを発火します。 @@ -62,14 +64,6 @@ export const execute = ( } } else { - - // 選択中のTextFieldがある場合はフォーカスを解除します。 - const selectedTextField = $getSelectedTextField(); - if (selectedTextField) { - selectedTextField.focus = false; - $setSelectedTextField(null); - } - // ステージ全体のポインターダウンイベントを発火します。 if ($stage.willTrigger(PointerEvent.POINTER_DOWN)) { $stage.dispatchEvent( diff --git a/packages/core/src/Player/usecase/PlayerBootUseCase.ts b/packages/core/src/Player/usecase/PlayerBootUseCase.ts new file mode 100644 index 00000000..1fbaf946 --- /dev/null +++ b/packages/core/src/Player/usecase/PlayerBootUseCase.ts @@ -0,0 +1,40 @@ +import type { IPlayerOptions } from "../../interface/IPlayerOptions"; +import { $player } from "../../Player"; +import { execute as playerCreateContainerElementService } from "../service/PlayerCreateContainerElementService"; +import { execute as playerApplyContainerElementStyleService } from "../service/PlayerApplyContainerElementStyleService"; +import { execute as playerLoadingAnimationService } from "../service/PlayerLoadingAnimationService"; +import { execute as playerResizeEventService } from "./PlayerResizeEventUseCase"; +import { execute as playerResizeRegisterService } from "./PlayerResizeRegisterUseCase"; + +/** + * @description Playerの初期起動処理 + * Initial startup processing of Player + * + * @param {IPlayerOptions} [options=null] + * @return {void} + * @method + * @protected + */ +export const execute = (options: IPlayerOptions | null = null): void => +{ + $player.setOptions(options); + + // create element + const element = playerCreateContainerElementService(); + + // apply base style + playerApplyContainerElementStyleService( + element, $player.fixedWidth, $player.fixedHeight + ); + + // start loading + playerLoadingAnimationService(element); + + // register resize event + if (!$player.fixedWidth && !$player.fixedHeight) { + playerResizeRegisterService(); + } + + // initialize resize + playerResizeEventService(); +}; \ No newline at end of file diff --git a/packages/text/src/TextArea/usecase/TextAreaRegisterEventUseCase.ts b/packages/text/src/TextArea/usecase/TextAreaRegisterEventUseCase.ts index edb23388..8b9cd30c 100644 --- a/packages/text/src/TextArea/usecase/TextAreaRegisterEventUseCase.ts +++ b/packages/text/src/TextArea/usecase/TextAreaRegisterEventUseCase.ts @@ -2,7 +2,6 @@ import { execute as textAreaCompositionStartUseCase } from "./TextAreaCompositio import { execute as textAreaCompositionUpdateUseCase } from "./TextAreaCompositionUpdateUseCase"; import { execute as textAreaCompositionEndUseCase } from "./TextAreaCompositionEndUseCase"; import { execute as textAreaInputUseCase } from "./TextAreaInputUseCase"; -import { execute as textAreaKeydownUseCase } from "./TextAreaKeydownUseCase"; /** * @description テキストエリアにイベントを登録します。 @@ -22,7 +21,4 @@ export const execute = (text_area: HTMLTextAreaElement): void => // input event text_area.addEventListener("input", textAreaInputUseCase); - - // keydown event - text_area.addEventListener("keydown", textAreaKeydownUseCase); }; \ No newline at end of file diff --git a/packages/text/src/TextField.ts b/packages/text/src/TextField.ts index 05a42992..2eebf493 100644 --- a/packages/text/src/TextField.ts +++ b/packages/text/src/TextField.ts @@ -20,6 +20,9 @@ import { execute as textFieldCopyUseCase } from "./TextField/usecase/TextFieldCo import { execute as textFieldInsertTextUseCase } from "./TextField/usecase/TextFieldInsertTextUseCase"; import { execute as textFieldApplyChangesService } from "./TextField/service/TextFieldApplyChangesService"; import { execute as textFieldSetFocusIndexUseCase } from "./TextField/usecase/TextFieldSetFocusIndexUseCase"; +import { execute as textFieldKeyDownEventUseCase } from "./TextField/usecase/TextFieldKeyDownEventUseCase"; +import { execute as textFieldDeleteTextUseCase } from "./TextField/usecase/TextFieldDeleteTextUseCase"; +import { execute as textFieldSelectAllUseCase } from "./TextField/usecase/TextFieldSelectAllUseCase"; import { $clamp, $toColorInt @@ -1137,6 +1140,33 @@ export class TextField extends InteractiveObject this.text = currentText + `${new_text}`; } + /** + * @description テキストフィールドのフォーカス位置にテキスtを追加します。 + * Adds text to the focus position of the text field. + * + * @param {string} new_text + * @return {void} + * @method + * @public + */ + insertText (new_text: string): void + { + textFieldInsertTextUseCase(this, new_text); + } + + /** + * @description テキストフィールドの選択範囲を削除します。 + * Deletes the selection range of the text field. + * + * @return {void} + * @method + * @public + */ + deleteText (): void + { + textFieldDeleteTextUseCase(this); + } + /** * @description lineIndex パラメーターで指定された行のテキストを返します。 * Returns the text of the line specified by the lineIndex parameter. @@ -1180,13 +1210,7 @@ export class TextField extends InteractiveObject */ selectAll (): void { - const textData = textFieldGetTextDataUseCase(this); - if (2 > textData.textTable.length) { - return ; - } - - this.selectIndex = 1; - this.focusIndex = textData.textTable.length; + textFieldSelectAllUseCase(this); } /** @@ -1218,7 +1242,7 @@ export class TextField extends InteractiveObject if (!this._$copyText || this.focusIndex === -1) { return ; } - textFieldInsertTextUseCase(this, this._$copyText); + this.insertText(this._$copyText); } /** @@ -1230,7 +1254,7 @@ export class TextField extends InteractiveObject * @param {boolean} [selected=false] * @return {void} * @method - * @private + * @public */ setFocusIndex ( stage_x: number, @@ -1239,4 +1263,18 @@ export class TextField extends InteractiveObject ): void { textFieldSetFocusIndexUseCase(this, stage_x, stage_y, selected); } + + /** + * @description キーダウンイベントを処理します。 + * Processes the key down event. + * + * @param {KeyboardEvent} event + * @return {void} + * @method + * @public + */ + keyDown (event: KeyboardEvent): void + { + textFieldKeyDownEventUseCase(this, event); + } } \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldArrowDownUseCase.ts b/packages/text/src/TextField/usecase/TextFieldArrowDownUseCase.ts index 41239641..58856122 100644 --- a/packages/text/src/TextField/usecase/TextFieldArrowDownUseCase.ts +++ b/packages/text/src/TextField/usecase/TextFieldArrowDownUseCase.ts @@ -1,4 +1,5 @@ import type { TextField } from "../../TextField"; +import { $textArea } from "../../TextUtil"; import { execute as textFieldGetTextDataUseCase } from "../../TextField/usecase/TextFieldGetTextDataUseCase"; import { execute as textFieldBlinkingClearTimeoutService } from "../../TextField/service/TextFieldBlinkingClearTimeoutService"; import { execute as textFieldBlinkingUseCase } from "../../TextField/usecase/TextFieldBlinkingUseCase"; @@ -8,11 +9,12 @@ import { execute as textFieldBlinkingUseCase } from "../../TextField/usecase/Tex * Moves the focus index of the text field down. * * @param {TextField} text_field + * @param {boolean} shift_key * @return {void} * @method * @protected */ -export const execute = (text_field: TextField): void => +export const execute = (text_field: TextField, shift_key: boolean): void => { if (text_field.focusIndex === -1) { return ; @@ -32,7 +34,24 @@ export const execute = (text_field: TextField): void => ? textObject.line : textObject.line - 1; + // 最終行の場合は、フォーカスインデックスを最終位置に設定 if (line === textData.lineTable.length - 1) { + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex; + } else { + if (text_field.selectIndex === text_field.focusIndex) { + text_field.selectIndex = -1; + } + } + } + + text_field.focusVisible = false; + text_field.focusIndex = textData.textTable.length; + textFieldBlinkingClearTimeoutService(); + textFieldBlinkingUseCase(text_field); return ; } @@ -72,8 +91,21 @@ export const execute = (text_field: TextField): void => } if (textObject.line > targetLine) { - text_field.focusIndex = textObject.mode === "text" ? idx - 1 : idx; - text_field.selectIndex = -1; + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex; + } else { + if (text_field.selectIndex === text_field.focusIndex) { + text_field.selectIndex = -1; + } + } + } + + $textArea.style.top = `${$textArea.offsetTop + textObject.h}px`; + text_field.focusVisible = false; + text_field.focusIndex = textObject.mode === "text" ? idx - 1 : idx; textFieldBlinkingClearTimeoutService(); textFieldBlinkingUseCase(text_field); return ; @@ -85,16 +117,38 @@ export const execute = (text_field: TextField): void => textWidth += textObject.w; if (textWidth > currentWidth) { - text_field.focusIndex = idx; - text_field.selectIndex = -1; + + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex; + } else { + if (text_field.selectIndex === idx - 1) { + text_field.selectIndex = -1; + } + } + } + + $textArea.style.top = `${$textArea.offsetTop + textObject.h}px`; + text_field.focusVisible = false; + text_field.focusIndex = idx; textFieldBlinkingClearTimeoutService(); textFieldBlinkingUseCase(text_field); return ; } } - text_field.focusIndex = textData.textTable.length; - text_field.selectIndex = -1; + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex; + } + } + + text_field.focusVisible = false; + text_field.focusIndex = textData.textTable.length; textFieldBlinkingClearTimeoutService(); textFieldBlinkingUseCase(text_field); }; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldArrowLeftUseCase.ts b/packages/text/src/TextField/usecase/TextFieldArrowLeftUseCase.ts index dcd0f918..15d3cedf 100644 --- a/packages/text/src/TextField/usecase/TextFieldArrowLeftUseCase.ts +++ b/packages/text/src/TextField/usecase/TextFieldArrowLeftUseCase.ts @@ -8,11 +8,12 @@ import { execute as textFieldBlinkingUseCase } from "../../TextField/usecase/Tex * Moves the focus index of the text field to the left. * * @param {TextField} text_field + * @param {boolean} shift_key * @return {void} * @method * @protected */ -export const execute = (text_field: TextField): void => +export const execute = (text_field: TextField, shift_key: boolean): void => { if (!text_field.focusIndex) { return ; @@ -24,8 +25,21 @@ export const execute = (text_field: TextField): void => return ; } + // fixed logic text_field.focusIndex--; - text_field.selectIndex = -1; + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex; + } else { + if (text_field.selectIndex === text_field.focusIndex) { + text_field.selectIndex = -1; + } + } + } + + text_field.focusVisible = false; textFieldBlinkingClearTimeoutService(); textFieldBlinkingUseCase(text_field); }; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldArrowRightUseCase.ts b/packages/text/src/TextField/usecase/TextFieldArrowRightUseCase.ts index c593c419..63c50bb6 100644 --- a/packages/text/src/TextField/usecase/TextFieldArrowRightUseCase.ts +++ b/packages/text/src/TextField/usecase/TextFieldArrowRightUseCase.ts @@ -8,19 +8,33 @@ import { execute as textFieldBlinkingUseCase } from "../../TextField/usecase/Tex * Moves the focus position of the text field to the right. * * @param {TextField} text_field + * @param {boolean} shift_key * @return {void} * @method * @protected */ -export const execute = (text_field: TextField): void => +export const execute = (text_field: TextField, shift_key: boolean): void => { const textData = textFieldGetTextDataUseCase(text_field); if (textData.textTable.length === text_field.focusIndex) { return ; } + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex; + } else { + if (text_field.selectIndex === text_field.focusIndex) { + text_field.selectIndex = -1; + } + } + } + + // fixed logic + text_field.focusVisible = false; text_field.focusIndex++; - text_field.selectIndex = -1; textFieldBlinkingClearTimeoutService(); textFieldBlinkingUseCase(text_field); }; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldArrowUpUseCase.ts b/packages/text/src/TextField/usecase/TextFieldArrowUpUseCase.ts index 13b1e6ac..4699c9fe 100644 --- a/packages/text/src/TextField/usecase/TextFieldArrowUpUseCase.ts +++ b/packages/text/src/TextField/usecase/TextFieldArrowUpUseCase.ts @@ -1,4 +1,5 @@ import type { TextField } from "../../TextField"; +import { $textArea } from "../../TextUtil"; import { execute as textFieldGetTextDataUseCase } from "../../TextField/usecase/TextFieldGetTextDataUseCase"; import { execute as textFieldBlinkingClearTimeoutService } from "../../TextField/service/TextFieldBlinkingClearTimeoutService"; import { execute as textFieldBlinkingUseCase } from "../../TextField/usecase/TextFieldBlinkingUseCase"; @@ -8,11 +9,12 @@ import { execute as textFieldBlinkingUseCase } from "../../TextField/usecase/Tex * Moves the focus index of the text field up. * * @param {TextField} text_field + * @param {boolean} shift_key * @return {void} * @method * @protected */ -export const execute = (text_field: TextField): void => +export const execute = (text_field: TextField, shift_key: boolean): void => { if (text_field.focusIndex === -1) { return ; @@ -29,6 +31,22 @@ export const execute = (text_field: TextField): void => const textObject = textData.textTable[index]; if (!textObject || !textObject.line) { + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex; + } else { + if (text_field.selectIndex === text_field.focusIndex) { + text_field.selectIndex = -1; + } + } + } + + text_field.focusVisible = false; + text_field.focusIndex = 1; + textFieldBlinkingClearTimeoutService(); + textFieldBlinkingUseCase(text_field); return ; } @@ -72,8 +90,22 @@ export const execute = (text_field: TextField): void => } if (textObject.line > targetLine) { - text_field.focusIndex = textObject.mode === "text" ? idx - 1 : idx; - text_field.selectIndex = -1; + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex; + } else { + if (text_field.selectIndex === text_field.focusIndex) { + text_field.selectIndex = -1; + } + } + } + + // fixed logic + $textArea.style.top = `${$textArea.offsetTop - textObject.h}px`; + text_field.focusVisible = false; + text_field.focusIndex = textObject.mode === "text" ? idx - 1 : idx; textFieldBlinkingClearTimeoutService(); textFieldBlinkingUseCase(text_field); return ; @@ -85,8 +117,22 @@ export const execute = (text_field: TextField): void => textWidth += textObject.w; if (textWidth > currentWidth) { - text_field.focusIndex = idx; - text_field.selectIndex = -1; + if (!shift_key) { + text_field.selectIndex = -1; + } else { + if (text_field.selectIndex === -1) { + text_field.selectIndex = text_field.focusIndex - 1; + } else { + if (text_field.selectIndex === idx + 1) { + text_field.selectIndex = -1; + } + } + } + + // fixed logic + $textArea.style.top = `${$textArea.offsetTop - textObject.h}px`; + text_field.focusVisible = false; + text_field.focusIndex = idx + 1; textFieldBlinkingClearTimeoutService(); textFieldBlinkingUseCase(text_field); return ; diff --git a/packages/text/src/TextField/usecase/TextFieldCopyUseCase.test.ts b/packages/text/src/TextField/usecase/TextFieldCopyUseCase.test.ts index aec762e6..fcfd4cbd 100644 --- a/packages/text/src/TextField/usecase/TextFieldCopyUseCase.test.ts +++ b/packages/text/src/TextField/usecase/TextFieldCopyUseCase.test.ts @@ -30,4 +30,18 @@ describe("TextFieldCopyUseCase.js test", () => expect(execute(textField)).toBe("かきくけこ"); }); + + it("execute test case2", () => + { + const textField = new TextField(); + textField.multiline = true; + textField.focusIndex = 1; + textField.selectIndex = 99; + + textField.text = `あいうえお +かきくけこ +さしすせそ`; + + expect(execute(textField)).toBe("あいうえお\nかきくけこ\nさしすせそ"); + }); }); \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldInsertTextUseCase.ts b/packages/text/src/TextField/usecase/TextFieldInsertTextUseCase.ts index cae7afd8..cd0dc744 100644 --- a/packages/text/src/TextField/usecase/TextFieldInsertTextUseCase.ts +++ b/packages/text/src/TextField/usecase/TextFieldInsertTextUseCase.ts @@ -27,6 +27,10 @@ export const execute = (text_field: TextField, texts: string): void => } const textData = textFieldGetTextDataUseCase(text_field); + if (2 > textData.textTable.length) { + text_field.appendText(texts); + return ; + } const textFormats: TextFormat[] = []; @@ -76,7 +80,7 @@ export const execute = (text_field: TextField, texts: string): void => } for (let idx = 0; idx < texts.length; ++idx) { - textFormats.push(new TextFormat(...Object.values(textFormat))); + textFormats.push(new TextFormat(...Object.values(textFormat.toObject()))); newText += texts[idx]; } } diff --git a/packages/text/src/TextArea/usecase/TextAreaKeydownUseCase.ts b/packages/text/src/TextField/usecase/TextFieldKeyDownEventUseCase.ts similarity index 59% rename from packages/text/src/TextArea/usecase/TextAreaKeydownUseCase.ts rename to packages/text/src/TextField/usecase/TextFieldKeyDownEventUseCase.ts index 1453e958..cb48cc47 100644 --- a/packages/text/src/TextArea/usecase/TextAreaKeydownUseCase.ts +++ b/packages/text/src/TextField/usecase/TextFieldKeyDownEventUseCase.ts @@ -1,25 +1,22 @@ - -import { $getSelectedTextField } from "../../TextUtil"; -import { execute as textFieldDeleteTextUseCase } from "../../TextField/usecase/TextFieldDeleteTextUseCase"; -import { execute as textFieldInsertTextUseCase } from "../../TextField/usecase/TextFieldInsertTextUseCase"; +import type { TextField } from "../../TextField"; +import { execute as textFieldArrowDownUseCase } from "../../TextField/usecase/TextFieldArrowDownUseCase"; import { execute as textFieldArrowUpUseCase } from "../../TextField/usecase/TextFieldArrowUpUseCase"; import { execute as textFieldArrowLeftUseCase } from "../../TextField/usecase/TextFieldArrowLeftUseCase"; import { execute as textFieldArrowRightUseCase } from "../../TextField/usecase/TextFieldArrowRightUseCase"; -import { execute as textFieldArrowDownUseCase } from "../../TextField/usecase/TextFieldArrowDownUseCase"; /** - * @description キーダウンイベントを処理します。 - * Processes the keydown event. + * @description テキストフィールドのキーボードダウンイベントを実行する + * Execute the keyboard down event of the text field * + * @param {TextField} text_field * @param {KeyboardEvent} event * @return {void} * @method * @protected */ -export const execute = (event: KeyboardEvent): void => +export const execute = (text_field: TextField, event: KeyboardEvent): void => { - const textField = $getSelectedTextField(); - if (!textField) { + if (text_field.focusIndex === -1) { return ; } @@ -27,47 +24,47 @@ export const execute = (event: KeyboardEvent): void => case "Backspace": case "Delete": - textFieldDeleteTextUseCase(textField); + text_field.deleteText(); break; case "Enter": - textFieldInsertTextUseCase(textField, "\n"); + text_field.insertText("\n"); break; case "ArrowLeft": - textFieldArrowLeftUseCase(textField); + textFieldArrowLeftUseCase(text_field, event.shiftKey); break; case "ArrowRight": - textFieldArrowRightUseCase(textField); + textFieldArrowRightUseCase(text_field, event.shiftKey); break; case "ArrowUp": - textFieldArrowUpUseCase(textField); + textFieldArrowUpUseCase(text_field, event.shiftKey); break; case "ArrowDown": - textFieldArrowDownUseCase(textField); + textFieldArrowDownUseCase(text_field, event.shiftKey); break; case "a": if (event.metaKey || event.ctrlKey) { event.preventDefault(); - textField.selectAll(); + text_field.selectAll(); } break; - case "v": + case "c": if (event.metaKey || event.ctrlKey) { event.preventDefault(); - textField.paste(); + text_field.copy(); } break; - case "c": + case "v": if (event.metaKey || event.ctrlKey) { event.preventDefault(); - textField.copy(); + text_field.paste(); } break; diff --git a/packages/text/src/TextField/usecase/TextFieldSelectAllUseCase.ts b/packages/text/src/TextField/usecase/TextFieldSelectAllUseCase.ts new file mode 100644 index 00000000..1b0cd6c4 --- /dev/null +++ b/packages/text/src/TextField/usecase/TextFieldSelectAllUseCase.ts @@ -0,0 +1,24 @@ +import type { TextField } from "@next2d/text"; +import { execute as textFieldApplyChangesService } from "../service/TextFieldApplyChangesService"; +import { execute as textFieldGetTextDataUseCase } from "./TextFieldGetTextDataUseCase"; + +/** + * @description テキストフィールドの全選択を実行する + * Execute the text field's select all + * + * @param {TextField} text_field + * @return {void} + * @method + * @protected + */ +export const execute = (text_field: TextField): void => +{ + const textData = textFieldGetTextDataUseCase(text_field); + if (2 > textData.textTable.length) { + return ; + } + + text_field.selectIndex = 1; + text_field.focusIndex = textData.textTable.length; + textFieldApplyChangesService(text_field); +}; \ No newline at end of file diff --git a/packages/text/src/TextField/usecase/TextFieldSetFocusUseCase.ts b/packages/text/src/TextField/usecase/TextFieldSetFocusUseCase.ts index 932161dd..63d92990 100644 --- a/packages/text/src/TextField/usecase/TextFieldSetFocusUseCase.ts +++ b/packages/text/src/TextField/usecase/TextFieldSetFocusUseCase.ts @@ -25,7 +25,11 @@ export const execute = (text_field: TextField, name: string): void => $textArea.value = ""; if (text_field.focus) { - $textArea.focus(); + + setTimeout((): void => + { + $textArea.focus(); + }, 300); if ($getBlinkingTimerId() === undefined) { if (text_field.focusIndex === -1) { diff --git a/packages/text/src/TextUtil.ts b/packages/text/src/TextUtil.ts index 259b2215..4e049d17 100644 --- a/packages/text/src/TextUtil.ts +++ b/packages/text/src/TextUtil.ts @@ -54,9 +54,9 @@ style += "position: fixed;"; style += "top: 0;"; style += "left: 0;"; style += "font-size: 16px;"; -style += "border: 0;"; +// style += "border: 0;"; style += "resize: none;"; -style += "opacity: 0;"; +// style += "opacity: 0;"; style += "z-index: -1;"; style += "pointer-events: none;"; $textArea.setAttribute("style", style);