diff --git a/BUILD.gn b/BUILD.gn index c681d2ab2f1976b97c67999d9ce3b90ed4dee95b..ead92005589baca5ea87d1e2f4ada91c7adcb767 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -14,6 +14,7 @@ group("advanced_ui_component") { deps = [ "atomicservicenavigation/interfaces:atomicservicenavigation", + "atomicservicesearch/interfaces:atomicservicesearch", "atomicservicetabs/interfaces:atomicservicetabs", "atomicserviceweb/interfaces:atomicserviceweb", "innerfullscreenlaunchcomponent/interfaces:innerfullscreenlaunchcomponent", diff --git a/atomicservicesearch/interfaces/BUILD.gn b/atomicservicesearch/interfaces/BUILD.gn new file mode 100644 index 0000000000000000000000000000000000000000..9fdf586c6db154009da849732736b4479b9b0840 --- /dev/null +++ b/atomicservicesearch/interfaces/BUILD.gn @@ -0,0 +1,57 @@ +# Copyright (c) 2025 Huawei Device Co., Ltd. +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import("//build/config/components/ets_frontend/es2abc_config.gni") +import("//build/ohos.gni") +import("//foundation/arkui/advanced_ui_component/atomicservice_config.gni") + +es2abc_gen_abc("gen_atomicservicesearch_abc") { + src_js = rebase_path("atomicservicesearch.js") + dst_file = rebase_path(target_out_dir + "/atomicservicesearch.abc") + in_puts = [ "atomicservicesearch.js" ] + out_puts = [ target_out_dir + "/atomicservicesearch.abc" ] + extra_args = [ "--module" ] +} + +gen_js_obj("atomicservicesearch_abc") { + input = get_label_info(":gen_atomicservicesearch_abc", "target_out_dir") + + "/atomicservicesearch.abc" + output = target_out_dir + "/atomicservicesearch_abc.o" + dep = ":gen_atomicservicesearch_abc" +} + +gen_obj("atomicservicesearch_abc_preview") { + input = get_label_info(":gen_atomicservicesearch_abc", "target_out_dir") + + "/atomicservicesearch.abc" + output = target_out_dir + "/atomicservicesearch_abc.c" + snapshot_dep = [ ":gen_atomicservicesearch_abc" ] +} + +ohos_shared_library("atomicservicesearch") { + sources = [ "atomicservicesearch.cpp" ] + + if (use_mingw_win || use_mac || use_linux) { + deps = [ ":gen_obj_src_atomicservicesearch_abc_preview" ] + } else { + deps = [ ":atomicservicesearch_abc" ] + } + + external_deps = [ + "hilog:libhilog", + "napi:ace_napi", + ] + + relative_install_dir = "module/atomicservice" + subsystem_name = "arkui" + part_name = "advanced_ui_component" +} diff --git a/atomicservicesearch/interfaces/atomicservicesearch.cpp b/atomicservicesearch/interfaces/atomicservicesearch.cpp new file mode 100644 index 0000000000000000000000000000000000000000..fd9e9ec22c24dcf1e3c343d14f430e22adac0ae7 --- /dev/null +++ b/atomicservicesearch/interfaces/atomicservicesearch.cpp @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "native_engine/native_engine.h" + +#include "napi/native_api.h" +#include "napi/native_node_api.h" + +extern const char _binary_atomicservicesearch_abc_start[]; +extern const char _binary_atomicservicesearch_abc_end[]; + +// Napi get abc code function +extern "C" __attribute__((visibility("default"))) +void NAPI_atomicservice_AtomicServiceSearch_GetABCCode(const char **buf, int *buflen) +{ + if (buf != nullptr) { + *buf = _binary_atomicservicesearch_abc_start; + } + if (buflen != nullptr) { + *buflen = _binary_atomicservicesearch_abc_end - _binary_atomicservicesearch_abc_start; + } +} + +/* + * Module define + */ +static napi_module AtomicServiceSearchModule = { + .nm_version = 1, + .nm_flags = 0, + .nm_filename = nullptr, + .nm_modname = "atomicservice.AtomicServiceSearch", + .nm_priv = ((void*)0), + .reserved = { 0 }, +}; + +/* + * Module registerfunction + */ +extern "C" __attribute__((constructor)) void AtomicServiceSearchRegisterModule(void) +{ + napi_module_register(&AtomicServiceSearchModule); +} \ No newline at end of file diff --git a/atomicservicesearch/interfaces/atomicservicesearch.js b/atomicservicesearch/interfaces/atomicservicesearch.js new file mode 100644 index 0000000000000000000000000000000000000000..e1e0ec2c45a506005dcbbe75b78b467b2da150ef --- /dev/null +++ b/atomicservicesearch/interfaces/atomicservicesearch.js @@ -0,0 +1,555 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +if (!('finalizeConstruction' in ViewPU.prototype)) { + Reflect.set(ViewPU.prototype, 'finalizeConstruction', () => { }); +} + +const LengthMetrics = requireNapi('arkui.node').LengthMetrics; +const TEXT_SIZE_BODY1 = { 'id': -1, 'type': -1, params: [`sys.float.ohos_id_text_size_body1`], 'bundleName': '__harDefaultBundleName__', 'moduleName': '__harDefaultModuleName__' }; +const COLOR_TEXT_SECONDARY = { 'id': -1, 'type': -1, params: [`sys.color.ohos_id_color_text_secondary`], 'bundleName': '__harDefaultBundleName__', 'moduleName': '__harDefaultModuleName__' }; +const ICON_COLOR_SECONDARY = { 'id': -1, 'type': 10001, params: ['sys.color.ohos_id_color_secondary'], 'bundleName': '__harDefaultBundleName__', 'moduleName': '__harDefaultModuleName__' }; +const ATOMIC_SERVICE_SEARCH_BG_COLOR = { 'id': -1, 'type': -1, params: [`sys.color.ohos_id_color_text_field_sub_bg`], 'bundleName': '__harDefaultBundleName__', 'moduleName': '__harDefaultModuleName__' }; +const TEXT_COLOR_PRIMARY = { 'id': -1, 'type': -1, params: [`sys.color.ohos_id_color_text_primary`], 'bundleName': '__harDefaultBundleName__', 'moduleName': '__harDefaultModuleName__' }; +const FUNCTION_ICON_COLOR = { 'id': -1, 'type': -1, params: [`sys.color.ohos_id_color_primary`], 'bundleName': '__harDefaultBundleName__', 'moduleName': '__harDefaultModuleName__' }; +const EFFECT_COLOR = { 'id': -1, 'type': -1, params: [`sys.color.ohos_id_color_click_effect`], 'bundleName': '__harDefaultBundleName__', 'moduleName': '__harDefaultModuleName__' }; +const ICON_SIZE = 16; +const SELECT_PADDING_LEFT = 6; +const SELECT_MARGIN_LEFT = 2; +const FLEX_SHRINK = 0; +const DIVIDER_OPACITY = 0.5; +const DIVIDER_MARGIN_LEFT = 2; +const DIVIDER_MARGIN_RIGHT = -2; +const ATOMIC_SERVICE_SEARCH_HEIGHT = 40; +const ATOMIC_SELECT_HEIGHT = 36; +const ATOMIC_SELECT_BORDER_RADIUS = 20; +const ATOMIC_DIVIDER_HEIGHT = 20; +const ICON_WIDTH_AND_HEIGTH = 24; +const OPERATION_ITEM1_MARGIN_RIGHT = 2; +const OPERATION_ITEM2_MARGIN_LEFT = 8; + +export class AtomicServiceSearch extends ViewPU { + + constructor(parent, params, __localStorage, elmtId = -1, paramsLambda = undefined, extraInfo) { + super(parent, __localStorage, elmtId, extraInfo); + if (typeof paramsLambda === 'function') { + this.paramsGenerator_ = paramsLambda; + } + this.__isFunction1Pressed = new ObservedPropertySimplePU(false, this, 'isFunction1Pressed'); + this.__isFunction2Pressed = new ObservedPropertySimplePU(false, this, 'isFunction2Pressed'); + this.__isSearchPressed = new ObservedPropertySimplePU(false, this, 'isSearchPressed'); + this.__showImage = new ObservedPropertySimplePU(true, this, 'showImage'); + this.__value = new SynchedPropertyObjectOneWayPU(params.value, this, 'value'); + this.__placeholder = new SynchedPropertyObjectOneWayPU(params.placeholder, this, 'placeholder'); + this.__select = new SynchedPropertyObjectOneWayPU(params.select, this, 'select'); + this.__search = new SynchedPropertyObjectOneWayPU(params.search, this, 'search'); + this.operation = undefined; + this.controller = new SearchController(); + this.setInitiallyProvidedValue(params); + this.declareWatch('value', this.onParamsChange); + this.declareWatch('select', this.onSelectChange); + this.declareWatch('search', this.onSearchChange); + this.finalizeConstruction(); + } + + setInitiallyProvidedValue(params) { + if (params.isFunction1Pressed !== undefined) { + this.isFunction1Pressed = params.isFunction1Pressed; + } + if (params.isFunction2Pressed !== undefined) { + this.isFunction2Pressed = params.isFunction2Pressed; + } + if (params.isSearchPressed !== undefined) { + this.isSearchPressed = params.isSearchPressed; + } + if (params.showImage !== undefined) { + this.showImage = params.showImage; + } + if (params.value === undefined) { + this.__value.set(''); + } + if (params.placeholder === undefined) { + this.__placeholder.set('Search'); + } + if (params.select === undefined) { + this.__select.set({}); + } + if (params.search === undefined) { + this.__search.set({ + componentBackgroundColor: ATOMIC_SERVICE_SEARCH_BG_COLOR, + placeholderFont: { + size: TEXT_SIZE_BODY1, + }, + placeholderColor: COLOR_TEXT_SECONDARY, + textFont: { + size: TEXT_SIZE_BODY1, + }, + fontColor: COLOR_TEXT_SECONDARY, + searchIcon: { + size: ICON_SIZE, + color: ICON_COLOR_SECONDARY, + }, + pressedBackgroundColor: EFFECT_COLOR + }); + } + if (params.operation !== undefined) { + this.operation = params.operation; + } + if (params.controller !== undefined) { + this.controller = params.controller; + } + } + + updateStateVars(params) { + this.__value.reset(params.value); + this.__placeholder.reset(params.placeholder); + this.__select.reset(params.select); + this.__search.reset(params.search); + } + + purgeVariableDependenciesOnElmtId(rmElmtId) { + this.__isFunction1Pressed.purgeDependencyOnElmtId(rmElmtId); + this.__isFunction2Pressed.purgeDependencyOnElmtId(rmElmtId); + this.__isSearchPressed.purgeDependencyOnElmtId(rmElmtId); + this.__showImage.purgeDependencyOnElmtId(rmElmtId); + this.__value.purgeDependencyOnElmtId(rmElmtId); + this.__placeholder.purgeDependencyOnElmtId(rmElmtId); + this.__select.purgeDependencyOnElmtId(rmElmtId); + this.__search.purgeDependencyOnElmtId(rmElmtId); + } + + aboutToBeDeleted() { + this.__isFunction1Pressed.aboutToBeDeleted(); + this.__isFunction2Pressed.aboutToBeDeleted(); + this.__isSearchPressed.aboutToBeDeleted(); + this.__showImage.aboutToBeDeleted(); + this.__value.aboutToBeDeleted(); + this.__placeholder.aboutToBeDeleted(); + this.__select.aboutToBeDeleted(); + this.__search.aboutToBeDeleted(); + SubscriberManager.Get().delete(this.id__()); + this.aboutToBeDeletedInternal(); + } + + get isFunction1Pressed() { + return this.__isFunction1Pressed.get(); + } + + set isFunction1Pressed(newValue) { + this.__isFunction1Pressed.set(newValue); + } + + get isFunction2Pressed() { + return this.__isFunction2Pressed.get(); + } + + set isFunction2Pressed(newValue) { + this.__isFunction2Pressed.set(newValue); + } + + get isSearchPressed() { + return this.__isSearchPressed.get(); + } + + set isSearchPressed(newValue) { + this.__isSearchPressed.set(newValue); + } + + get showImage() { + return this.__showImage.get(); + } + + set showImage(newValue) { + this.__showImage.set(newValue); + } + + get value() { + return this.__value.get(); + } + + set value(newValue) { + this.__value.set(newValue); + } + + get placeholder() { + return this.__placeholder.get(); + } + + set placeholder(newValue) { + this.__placeholder.set(newValue); + } + + get select() { + return this.__select.get(); + } + + set select(newValue) { + this.__select.set(newValue); + } + + get search() { + return this.__search.get(); + } + + set search(newValue) { + this.__search.set(newValue); + } + + aboutToAppear() { + this.showImage = this.value?.toString().length === 0 ? true : false; + this.initSelectStyle(); + this.initSearchStyle(); + } + + onParamsChange() { + this.showImage = this.value?.toString().length === 0 ? true : false; + } + + onSelectChange() { + this.initSelectStyle(); + } + + onSearchChange() { + this.initSearchStyle(); + } + + initSelectStyle() { + if (typeof this.select !== 'undefined') { + if (typeof this.select.font === 'undefined') { + this.select.font = { size: TEXT_SIZE_BODY1 }; + } + if (typeof this.select.fontColor !== 'undefined') { + this.select.fontColor = TEXT_COLOR_PRIMARY; + } + } + } + + initSearchStyle() { + if (typeof this.search !== 'undefined') { + if (typeof this.search.componentBackgroundColor === 'undefined') { + this.search.componentBackgroundColor = ATOMIC_SERVICE_SEARCH_BG_COLOR; + } + if (typeof this.search.placeholderFont === 'undefined') { + this.search.placeholderFont = { size: TEXT_SIZE_BODY1 }; + } + if (typeof this.search.placeholderColor === 'undefined') { + this.search.placeholderColor = COLOR_TEXT_SECONDARY; + } + if (typeof this.search.textFont === 'undefined') { + this.search.textFont = { size: TEXT_SIZE_BODY1 }; + } + if (typeof this.search.fontColor === 'undefined') { + this.search.fontColor = COLOR_TEXT_SECONDARY; + } + if (typeof this.search.searchIcon === 'undefined') { + this.search.searchIcon = { + size: ICON_SIZE, + color: ICON_COLOR_SECONDARY, + }; + } + if (typeof this.search.pressedBackgroundColor === 'undefined') { + this.search.pressedBackgroundColor = EFFECT_COLOR; + } + } + } + + renderSelect(parent = null) { + this.observeComponentCreation2((elmtId, isInitialRender) => { + If.create(); + if (typeof this.select !== 'undefined' && typeof this.select.options !== 'undefined') { + this.ifElseBranchUpdateFunction(0, () => { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Row.create(); + Row.flexShrink(FLEX_SHRINK); + }, Row); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Select.create(this.select?.options); + Select.value(this.select?.value); + Select.selected(this.select?.selected); + Select.onSelect(this.select?.onSelect); + Select.controlSize(this.select?.controlSize); + Select.menuItemContentModifier.bind(this)(this.select?.menuItemContentModifier); + Select.divider(this.select?.divider); + Select.font(this.select?.font); + Select.fontColor(this.select?.fontColor); + Select.selectedOptionBgColor(this.select?.selectedOptionBgColor); + Select.selectedOptionFont(this.select?.selectedOptionFont); + Select.selectedOptionFontColor(this.select?.selectedOptionFontColor); + Select.optionBgColor(this.select?.optionBgColor); + Select.optionFont(this.select?.optionFont); + Select.optionFontColor(this.select?.optionFontColor); + Select.space(this.select?.space); + Select.arrowPosition(this.select?.arrowPosition); + Select.menuAlign(this.select?.menuAlign?.alignType, this.select?.menuAlign?.offset); + Select.optionWidth(this.select?.optionWidth); + Select.optionHeight(this.select?.optionHeight); + Select.menuBackgroundColor(this.select?.menuBackgroundColor); + Select.menuBackgroundBlurStyle(this.select?.menuBackgroundBlurStyle); + Select.height(ATOMIC_SELECT_HEIGHT); + Select.borderRadius(ATOMIC_SELECT_BORDER_RADIUS); + Select.constraintSize({ minHeight: ATOMIC_SELECT_HEIGHT }); + Select.padding({ start: LengthMetrics.vp(SELECT_PADDING_LEFT) }); + Select.margin({ start: LengthMetrics.vp(SELECT_MARGIN_LEFT) }); + Select.backgroundColor(Color.Transparent); + }, Select); + Select.pop(); + Row.pop(); + }); + } + else { + this.ifElseBranchUpdateFunction(1, () => { + }); + } + }, If); + If.pop(); + } + + renderDivider(parent = null) { + this.observeComponentCreation2((elmtId, isInitialRender) => { + If.create(); + if (typeof this.select !== 'undefined' && typeof this.select.options !== 'undefined') { + this.ifElseBranchUpdateFunction(0, () => { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Divider.create(); + Divider.vertical(true); + Divider.color(Color.Black); + Divider.height(ATOMIC_DIVIDER_HEIGHT); + Divider.opacity(DIVIDER_OPACITY); + Divider.margin({ + start: LengthMetrics.vp(DIVIDER_MARGIN_LEFT), + end: LengthMetrics.vp(DIVIDER_MARGIN_RIGHT) + }); + }, Divider); + }); + } + else { + this.ifElseBranchUpdateFunction(1, () => { + }); + } + }, If); + If.pop(); + } + + renderSearch(parent = null) { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Search.create({ + value: this.value?.toString(), + placeholder: this.placeholder, + controller: this.controller + }); + Search.backgroundColor(Color.Transparent); + Search.searchButton(this.search?.searchButton?.value, this.search?.searchButton?.option); + Search.placeholderColor(this.search?.placeholderColor); + Search.placeholderFont(this.search?.placeholderFont); + Search.textFont(this.search?.textFont); + Search.textAlign(this.search?.textAlign); + Search.copyOption(this.search?.copyOption); + Search.searchIcon(this.search?.searchIcon); + Search.cancelButton({ icon: this.search?.cancelIcon }); + Search.fontColor(this.search?.fontColor); + Search.caretStyle(this.search?.caretStyle); + Search.enableKeyboardOnFocus(this.search?.enableKeyboardOnFocus); + Search.selectionMenuHidden(this.search?.hideSelectionMenu); + Search.customKeyboard(null, { supportAvoidance: this.search?.avoidKeyboard }); + Search.type(this.search?.type); + Search.maxLength(this.search?.maxLength); + Search.enterKeyType(this.search?.enterKeyType); + Search.decoration(this.search?.decoration); + Search.letterSpacing(this.search?.letterSpacing); + Search.fontFeature(this.search?.fontFeature); + Search.selectedBackgroundColor(this.search?.selectedBackgroundColor); + Search.inputFilter(this.search?.inputFilter?.value, this.search?.inputFilter?.error); + Search.textIndent(this.search?.textIndent); + Search.minFontSize(this.search?.minFontSize); + Search.maxFontSize(this.search?.maxFontSize); + Search.editMenuOptions(this.search?.editMenuOptions); + Search.enablePreviewText(this.search?.enablePreviewText); + Search.enableHapticFeedback(this.search?.enableHapticFeedback); + Search.placeholderFont(this.search?.placeholderFont); + Search.textFont(this.search?.textFont); + Search.searchIcon(this.search?.searchIcon); + Search.fontColor(this.search?.fontColor); + Search.onCut(this.search?.onCut); + Search.onCopy(this.search?.onCopy); + Search.onPaste(this.search?.onPaste); + Search.onSubmit(this.search?.onSubmit); + Search.onDidInsert(this.search?.onDidInsert); + Search.onDidDelete(this.search?.onDidDelete); + Search.onEditChange(this.search?.onEditChange); + Search.onWillInsert(this.search?.onWillInsert); + Search.onWillDelete(this.search?.onWillDelete); + Search.onContentScroll(this.search?.onContentScroll); + Search.onTextSelectionChange(this.search?.onTextSelectionChange); + Search.onChange((value, previewText) => { + if (typeof this.search?.onChange !== 'undefined') { + this.search?.onChange(value, previewText); + } + this.value = value; + }); + Search.onTouch((event) => { + if (event && event.type === TouchType.Down) { + this.isSearchPressed = true; + } + else if (event && event.type === TouchType.Up) { + this.isSearchPressed = false; + } + }); + }, Search); + Search.pop(); + } + + renderAuxiliaryItem(parent = null) { + this.observeComponentCreation2((elmtId, isInitialRender) => { + If.create(); + if (typeof this.operation?.auxiliaryItem !== 'undefined' && this.showImage) { + this.ifElseBranchUpdateFunction(0, () => { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Row.create(); + Row.onClick(this.operation?.auxiliaryItem.action); + Row.flexShrink(FLEX_SHRINK); + Row.borderRadius(ATOMIC_SELECT_BORDER_RADIUS); + Row.alignItems(VerticalAlign.Center); + Row.justifyContent(FlexAlign.Center); + Row.width(ATOMIC_SELECT_HEIGHT); + Row.height(ATOMIC_SELECT_HEIGHT); + Row.margin({ right: OPERATION_ITEM1_MARGIN_RIGHT }); + Row.backgroundColor(this.isFunction1Pressed ? this.search?.pressedBackgroundColor : Color.Transparent); + Row.onTouch((event) => { + if (event && event.type === TouchType.Down) { + this.isFunction1Pressed = true; + } + else if (event && event.type === TouchType.Up) { + this.isFunction1Pressed = false; + } + }); + }, Row); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Image.create(this.operation?.auxiliaryItem?.value); + Image.objectFit(ImageFit.Contain); + Image.fillColor(FUNCTION_ICON_COLOR); + Image.width(ICON_WIDTH_AND_HEIGTH); + Image.height(ICON_WIDTH_AND_HEIGTH); + }, Image); + Row.pop(); + }); + } + else { + this.ifElseBranchUpdateFunction(1, () => { + }); + } + }, If); + If.pop(); + } + + renderIndependentItem(parent = null) { + this.observeComponentCreation2((elmtId, isInitialRender) => { + If.create(); + if (typeof this.operation?.independentItem !== 'undefined') { + this.ifElseBranchUpdateFunction(0, () => { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Row.create(); + Row.onClick(this.operation?.independentItem.action); + Row.flexShrink(FLEX_SHRINK); + Row.borderRadius(ATOMIC_SELECT_BORDER_RADIUS); + Row.alignItems(VerticalAlign.Center); + Row.justifyContent(FlexAlign.Center); + Row.width(ATOMIC_SERVICE_SEARCH_HEIGHT); + Row.height(ATOMIC_SERVICE_SEARCH_HEIGHT); + Row.margin(OPERATION_ITEM2_MARGIN_LEFT); + Row.backgroundColor(this.isFunction2Pressed ? + this.search?.pressedBackgroundColor : this.search?.componentBackgroundColor); + Row.onTouch((event) => { + if (event && event.type === TouchType.Down) { + this.isFunction2Pressed = true; + } + else if (event && event.type === TouchType.Up) { + this.isFunction2Pressed = false; + } + }); + }, Row); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Image.create(this.operation?.independentItem.value); + Image.objectFit(ImageFit.Contain); + Image.fillColor(FUNCTION_ICON_COLOR); + Image.width(ICON_WIDTH_AND_HEIGTH); + Image.height(ICON_WIDTH_AND_HEIGTH); + }, Image); + Row.pop(); + }); + } + else { + this.ifElseBranchUpdateFunction(1, () => { + }); + } + }, If); + If.pop(); + } + + initialRender() { + this.observeComponentCreation2((elmtId, isInitialRender) => { + Row.create(); + Row.height(ATOMIC_SERVICE_SEARCH_HEIGHT); + }, Row); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Flex.create({ + direction: FlexDirection.Row, + alignItems: ItemAlign.Center, + justifyContent: FlexAlign.Start + }); + }, Flex); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Stack.create(); + Stack.alignContent(Alignment.End); + Stack.borderRadius(ATOMIC_SELECT_BORDER_RADIUS); + Stack.backgroundColor(this.isSearchPressed ? + this.search?.pressedBackgroundColor : this.search?.componentBackgroundColor); + }, Stack); + this.observeComponentCreation2((elmtId, isInitialRender) => { + Flex.create({ + direction: FlexDirection.Row, + alignItems: ItemAlign.Center, + justifyContent: FlexAlign.Start + }); + }, Flex); + this.renderSelect.bind(this)(); + this.renderDivider.bind(this)(); + this.renderSearch.bind(this)(); + Flex.pop(); + this.observeComponentCreation2((elmtId, isInitialRender) => { + If.create(); + if (typeof this.search?.searchButton === 'undefined') { + this.ifElseBranchUpdateFunction(0, () => { + this.renderAuxiliaryItem.bind(this)(); + }); + } + else { + this.ifElseBranchUpdateFunction(1, () => { + }); + } + }, If); + If.pop(); + Stack.pop(); + this.renderIndependentItem.bind(this)(); + Flex.pop(); + Row.pop(); + } + + rerender() { + this.updateDirtyElements(); + } + +} + +export default { AtomicServiceSearch }; diff --git a/atomicservicesearch/source/atomicservicesearch.ets b/atomicservicesearch/source/atomicservicesearch.ets new file mode 100644 index 0000000000000000000000000000000000000000..63d00d21a04903c3651df9a7cb578b81a632ef31 --- /dev/null +++ b/atomicservicesearch/source/atomicservicesearch.ets @@ -0,0 +1,427 @@ +/* + * Copyright (c) 2025 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +import { LengthMetrics, OperationOption } from '@kit.ArkUI'; + +declare type OnSelectCallback = (index: number, value?: string) => void; +declare type OnPasteCallback = (value: string, event: PasteEvent) => void; +declare type OnTextSelectionChangeCallback = (selectionStart: number, selectionEnd: number) => void; +declare type OnContentScrollCallback = (totalOffsetX: number, totalOffsetY: number) => void; + +const TEXT_SIZE_BODY1: Resource = $r(`sys.float.ohos_id_text_size_body1`); +const COLOR_TEXT_SECONDARY = $r(`sys.color.ohos_id_color_text_secondary`); +const ICON_COLOR_SECONDARY = $r('sys.color.ohos_id_color_secondary'); +const ATOMIC_SERVICE_SEARCH_BG_COLOR = $r(`sys.color.ohos_id_color_text_field_sub_bg`); +const TEXT_COLOR_PRIMARY = $r(`sys.color.ohos_id_color_text_primary`); +const FUNCTION_ICON_COLOR = $r(`sys.color.ohos_id_color_primary`); +const EFFECT_COLOR = $r(`sys.color.ohos_id_color_click_effect`); +const ICON_SIZE: number = 16; +const SELECT_PADDING_LEFT: number = 6; +const SELECT_MARGIN_LEFT: number = 2; +const FLEX_SHRINK: number = 0; +const DIVIDER_OPACITY: number = 0.5; +const DIVIDER_MARGIN_LEFT: number = 2; +const DIVIDER_MARGIN_RIGHT: number = -2; +const ATOMIC_SERVICE_SEARCH_HEIGHT: number = 40; +const ATOMIC_SELECT_HEIGHT: number = 36; +const ATOMIC_SELECT_BORDER_RADIUS: number = 20; +const ATOMIC_DIVIDER_HEIGHT: number = 20; +const ICON_WIDTH_AND_HEIGTH: number = 24; +const OPERATION_ITEM1_MARGIN_RIGHT: number = 2; +const OPERATION_ITEM2_MARGIN_LEFT: number = 8; + +export interface InputFilterOptions { + value: ResourceStr, + error?: Callback +} + +export interface SearchButtonParams { + value: string, + option?: SearchButtonOptions +} + +export interface MenuAlignOption { + alignType: MenuAlignType, + offset?: Offset +} + +export interface SelectOptions { + options?: Array; + selected?: number; + value?: string; + onSelect?: OnSelectCallback; + controlSize?: ControlSize; + menuItemContentModifier?: ContentModifier; + divider?: Optional | null; + font?: Font; + fontColor?: ResourceColor; + selectedOptionBgColor?: ResourceColor; + selectedOptionFont?: Font; + selectedOptionFontColor?: ResourceColor; + optionBgColor?: ResourceColor; + optionFont?: Font; + optionFontColor?: ResourceColor; + optionWidth?: Dimension | OptionWidthMode; + optionHeight?: Dimension; + space?: Length; + arrowPosition?: ArrowPosition; + menuAlign?: MenuAlignOption; + menuBackgroundColor?: ResourceColor; + menuBackgroundBlurStyle?: BlurStyle; +} + +export interface SearchOptions { + componentBackgroundColor?: ResourceColor; + pressedBackgroundColor?: ResourceColor; + searchButton?: SearchButtonParams; + placeholderColor?: ResourceColor; + placeholderFont?: Font; + textFont?: Font; + textAlign?: TextAlign; + copyOption?: CopyOptions; + searchIcon?: IconOptions | SymbolGlyphModifier; + cancelIcon?: IconOptions; + fontColor?: ResourceColor; + caretStyle?: CaretStyle; + enableKeyboardOnFocus?: boolean; + hideSelectionMenu?: boolean; + avoidKeyboard?: boolean; + type?: SearchType; + maxLength?: number; + enterKeyType?: EnterKeyType; + decoration?: TextDecorationOptions; + letterSpacing?: number | string | Resource; + fontFeature?: string; + selectedBackgroundColor?: ResourceColor; + inputFilter?: InputFilterOptions; + textIndent?: Dimension; + minFontSize?: number | string | Resource; + maxFontSize?: number | string | Resource; + editMenuOptions?: EditMenuOptions; + enablePreviewText?: boolean; + enableHapticFeedback?: boolean; + onSubmit?: Callback | SearchSubmitCallback; + onChange?: EditableTextOnChangeCallback; + onCopy?: Callback; + onCut?: Callback; + onPaste?: OnPasteCallback; + onTextSelectionChange?: OnTextSelectionChangeCallback; + onContentScroll?: OnContentScrollCallback; + onEditChange?: Callback; + onWillInsert?: Callback; + onDidInsert?: Callback; + onWillDelete?: Callback; + onDidDelete?: Callback; +} + +export interface OperationOptions { + auxiliaryItem?: OperationOption; + independentItem?: OperationOption; +} + +@Component +export struct AtomicServiceSearch { + @State private isFunction1Pressed: boolean = false; + @State private isFunction2Pressed: boolean = false; + @State private isSearchPressed: boolean = false; + @State private showImage: boolean = true; + @Prop @Watch('onParamsChange') value?: ResourceStr = ''; + @Prop placeholder?: ResourceStr = 'Search'; + @Prop @Watch('onSelectChange') select?: SelectOptions = {}; + @Prop @Watch('onSearchChange') search?: SearchOptions = { + componentBackgroundColor: ATOMIC_SERVICE_SEARCH_BG_COLOR, + placeholderFont: { + size: TEXT_SIZE_BODY1, + }, + placeholderColor: COLOR_TEXT_SECONDARY, + textFont: { + size: TEXT_SIZE_BODY1, + }, + fontColor: COLOR_TEXT_SECONDARY, + searchIcon: { + size: ICON_SIZE, + color: ICON_COLOR_SECONDARY, + }, + pressedBackgroundColor: EFFECT_COLOR + }; + operation?: OperationOptions; + controller?: SearchController = new SearchController(); + + aboutToAppear(): void { + this.showImage = this.value?.toString().length === 0 ? true : false; + this.initSelectStyle(); + this.initSearchStyle(); + } + + private onParamsChange(): void { + this.showImage = this.value?.toString().length === 0 ? true : false; + } + + private onSelectChange(): void { + this.initSelectStyle(); + } + + private onSearchChange(): void { + this.initSearchStyle(); + } + + private initSelectStyle(): void { + if (typeof this.select !== 'undefined') { + if (typeof this.select.font === 'undefined') { + this.select.font = { size: TEXT_SIZE_BODY1 }; + } + if (typeof this.select.fontColor !== 'undefined') { + this.select.fontColor = TEXT_COLOR_PRIMARY; + } + } + } + + private initSearchStyle(): void { + if (typeof this.search !== 'undefined') { + if (typeof this.search.componentBackgroundColor === 'undefined') { + this.search.componentBackgroundColor = ATOMIC_SERVICE_SEARCH_BG_COLOR; + } + if (typeof this.search.placeholderFont === 'undefined') { + this.search.placeholderFont = { size: TEXT_SIZE_BODY1 }; + } + if (typeof this.search.placeholderColor === 'undefined') { + this.search.placeholderColor = COLOR_TEXT_SECONDARY; + } + if (typeof this.search.textFont === 'undefined') { + this.search.textFont = { size: TEXT_SIZE_BODY1 }; + } + if (typeof this.search.fontColor === 'undefined') { + this.search.fontColor = COLOR_TEXT_SECONDARY; + } + if (typeof this.search.searchIcon === 'undefined') { + this.search.searchIcon = { + size: ICON_SIZE, + color: ICON_COLOR_SECONDARY, + } + } + if (typeof this.search.pressedBackgroundColor === 'undefined') { + this.search.pressedBackgroundColor = EFFECT_COLOR; + } + } + } + + @Builder + renderSelect() { + if (typeof this.select !== 'undefined' && typeof this.select.options !== 'undefined') { + Row() { + Select(this.select?.options) + .value(this.select?.value) + .selected(this.select?.selected) + .onSelect(this.select?.onSelect) + .controlSize(this.select?.controlSize) + .menuItemContentModifier(this.select?.menuItemContentModifier) + .divider(this.select?.divider) + .font(this.select?.font) + .fontColor(this.select?.fontColor) + .selectedOptionBgColor(this.select?.selectedOptionBgColor) + .selectedOptionFont(this.select?.selectedOptionFont) + .selectedOptionFontColor(this.select?.selectedOptionFontColor) + .optionBgColor(this.select?.optionBgColor) + .optionFont(this.select?.optionFont) + .optionFontColor(this.select?.optionFontColor) + .space(this.select?.space) + .arrowPosition(this.select?.arrowPosition) + .menuAlign(this.select?.menuAlign?.alignType, this.select?.menuAlign?.offset) + .optionWidth(this.select?.optionWidth) + .optionHeight(this.select?.optionHeight) + .menuBackgroundColor(this.select?.menuBackgroundColor) + .menuBackgroundBlurStyle(this.select?.menuBackgroundBlurStyle) + .height(ATOMIC_SELECT_HEIGHT) + .borderRadius(ATOMIC_SELECT_BORDER_RADIUS) + .constraintSize({ minHeight: ATOMIC_SELECT_HEIGHT }) + .padding({ start: LengthMetrics.vp(SELECT_PADDING_LEFT) }) + .margin({ start: LengthMetrics.vp(SELECT_MARGIN_LEFT) }) + .backgroundColor(Color.Transparent) + } + .flexShrink(FLEX_SHRINK) + } + } + + @Builder + renderDivider() { + if (typeof this.select !== 'undefined' && typeof this.select.options !== 'undefined') { + Divider() + .vertical(true) + .color(Color.Black) + .height(ATOMIC_DIVIDER_HEIGHT) + .opacity(DIVIDER_OPACITY) + .margin({ + start: LengthMetrics.vp(DIVIDER_MARGIN_LEFT), + end: LengthMetrics.vp(DIVIDER_MARGIN_RIGHT) + }) + } + } + + @Builder + renderSearch() { + Search({ + value: this.value?.toString(), + placeholder: this.placeholder, + controller: this.controller + }) + .backgroundColor(Color.Transparent) + .searchButton(this.search?.searchButton?.value, this.search?.searchButton?.option) + .placeholderColor(this.search?.placeholderColor) + .placeholderFont(this.search?.placeholderFont) + .textFont(this.search?.textFont) + .textAlign(this.search?.textAlign) + .copyOption(this.search?.copyOption) + .searchIcon(this.search?.searchIcon) + .cancelButton({ icon: this.search?.cancelIcon }) + .fontColor(this.search?.fontColor) + .caretStyle(this.search?.caretStyle) + .enableKeyboardOnFocus(this.search?.enableKeyboardOnFocus) + .selectionMenuHidden(this.search?.hideSelectionMenu) + .customKeyboard(null, { supportAvoidance: this.search?.avoidKeyboard }) + .type(this.search?.type) + .maxLength(this.search?.maxLength) + .enterKeyType(this.search?.enterKeyType) + .decoration(this.search?.decoration) + .letterSpacing(this.search?.letterSpacing) + .fontFeature(this.search?.fontFeature) + .selectedBackgroundColor(this.search?.selectedBackgroundColor) + .inputFilter(this.search?.inputFilter?.value, this.search?.inputFilter?.error) + .textIndent(this.search?.textIndent) + .minFontSize(this.search?.minFontSize) + .maxFontSize(this.search?.maxFontSize) + .editMenuOptions(this.search?.editMenuOptions) + .enablePreviewText(this.search?.enablePreviewText) + .enableHapticFeedback(this.search?.enableHapticFeedback) + .placeholderFont(this.search?.placeholderFont) + .textFont(this.search?.textFont) + .searchIcon(this.search?.searchIcon) + .fontColor(this.search?.fontColor) + .onCut(this.search?.onCut) + .onCopy(this.search?.onCopy) + .onPaste(this.search?.onPaste) + .onSubmit(this.search?.onSubmit) + .onDidInsert(this.search?.onDidInsert) + .onDidDelete(this.search?.onDidDelete) + .onEditChange(this.search?.onEditChange) + .onWillInsert(this.search?.onWillInsert) + .onWillDelete(this.search?.onWillDelete) + .onContentScroll(this.search?.onContentScroll) + .onTextSelectionChange(this.search?.onTextSelectionChange) + .onChange((value: string, previewText?: PreviewText) => { + if (typeof this.search?.onChange !== 'undefined') { + this.search?.onChange(value, previewText); + } + this.value = value; + }) + .onTouch((event?: TouchEvent) => { + if (event && event.type === TouchType.Down) { + this.isSearchPressed = true; + } else if (event && event.type === TouchType.Up) { + this.isSearchPressed = false; + } + }) + } + + @Builder + renderAuxiliaryItem() { + if (typeof this.operation?.auxiliaryItem !== 'undefined' && this.showImage) { + Row() { + Image(this.operation?.auxiliaryItem?.value) + .objectFit(ImageFit.Contain) + .fillColor(FUNCTION_ICON_COLOR) + .width(ICON_WIDTH_AND_HEIGTH) + .height(ICON_WIDTH_AND_HEIGTH) + } + .onClick(this.operation?.auxiliaryItem.action) + .flexShrink(FLEX_SHRINK) + .borderRadius(ATOMIC_SELECT_BORDER_RADIUS) + .alignItems(VerticalAlign.Center) + .justifyContent(FlexAlign.Center) + .width(ATOMIC_SELECT_HEIGHT) + .height(ATOMIC_SELECT_HEIGHT) + .margin({ right: OPERATION_ITEM1_MARGIN_RIGHT }) + .backgroundColor(this.isFunction1Pressed ? this.search?.pressedBackgroundColor : Color.Transparent) + .onTouch((event?: TouchEvent) => { + if (event && event.type === TouchType.Down) { + this.isFunction1Pressed = true; + } else if (event && event.type === TouchType.Up) { + this.isFunction1Pressed = false; + } + }) + } + } + + @Builder + renderIndependentItem() { + if (typeof this.operation?.independentItem !== 'undefined') { + Row() { + Image(this.operation?.independentItem.value) + .objectFit(ImageFit.Contain) + .fillColor(FUNCTION_ICON_COLOR) + .width(ICON_WIDTH_AND_HEIGTH) + .height(ICON_WIDTH_AND_HEIGTH) + } + .onClick(this.operation?.independentItem.action) + .flexShrink(FLEX_SHRINK) + .borderRadius(ATOMIC_SELECT_BORDER_RADIUS) + .alignItems(VerticalAlign.Center) + .justifyContent(FlexAlign.Center) + .width(ATOMIC_SERVICE_SEARCH_HEIGHT) + .height(ATOMIC_SERVICE_SEARCH_HEIGHT) + .margin(OPERATION_ITEM2_MARGIN_LEFT) + .backgroundColor(this.isFunction2Pressed ? + this.search?.pressedBackgroundColor : this.search?.componentBackgroundColor) + .onTouch((event?: TouchEvent) => { + if (event && event.type === TouchType.Down) { + this.isFunction2Pressed = true; + } else if (event && event.type === TouchType.Up) { + this.isFunction2Pressed = false; + } + }) + } + } + + build() { + Row() { + Flex({ + direction: FlexDirection.Row, + alignItems: ItemAlign.Center, + justifyContent: FlexAlign.Start + }) { + Stack() { + Flex({ + direction: FlexDirection.Row, + alignItems: ItemAlign.Center, + justifyContent: FlexAlign.Start + }) { + this.renderSelect(); + this.renderDivider(); + this.renderSearch(); + } + + if (typeof this.search?.searchButton === 'undefined') { + this.renderAuxiliaryItem(); + } + } + .alignContent(Alignment.End) + .borderRadius(ATOMIC_SELECT_BORDER_RADIUS) + .backgroundColor(this.isSearchPressed ? + this.search?.pressedBackgroundColor : this.search?.componentBackgroundColor) + + this.renderIndependentItem(); + } + } + .height(ATOMIC_SERVICE_SEARCH_HEIGHT) + } +} diff --git a/bundle.json b/bundle.json index ca80fc0637ffa0f48a4c7f0c945dba0714254b9a..92b2aee4afe4dcccdb74439dcce38c1660764e43 100644 --- a/bundle.json +++ b/bundle.json @@ -26,6 +26,7 @@ "build": { "sub_component": [ "//foundation/arkui/advanced_ui_component/atomicservicenavigation/interfaces:atomicservicenavigation", + "//foundation/arkui/advanced_ui_component/atomicservicesearch/interfaces:atomicservicesearch", "//foundation/arkui/advanced_ui_component/atomicservicetabs/interfaces:atomicservicetabs", "//foundation/arkui/advanced_ui_component/atomicserviceweb/interfaces:atomicserviceweb", "//foundation/arkui/advanced_ui_component/innerfullscreenlaunchcomponent/interfaces:innerfullscreenlaunchcomponent",