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 #include "bridge/declarative_frontend/engine/jsi/nativeModule/arkts_native_water_flow_bridge.h"
16
17 #include "base/utils/string_utils.h"
18 #include "base/utils/utils.h"
19 #include "core/interfaces/native/node/api.h"
20 #include "bridge/declarative_frontend/engine/jsi/nativeModule/arkts_utils.h"
21
22 namespace OHOS::Ace::NG {
23 namespace {
24 constexpr double FRICTION_DEFAULT = -1.0;
25 constexpr double DIMENSION_DEFAULT = 0.0;
26 constexpr int32_t NUM_0 = 0;
27 constexpr int32_t NUM_1 = 1;
28 constexpr int32_t NUM_2 = 2;
29 constexpr int32_t NUM_3 = 3;
30 constexpr int32_t NUM_4 = 4;
31 const std::vector<FlexDirection> LAYOUT_DIRECTION = { FlexDirection::ROW, FlexDirection::COLUMN,
32 FlexDirection::ROW_REVERSE, FlexDirection::COLUMN_REVERSE };
33
SetItemConstraintSizeSendParams(CalcDimension & doubleValue,std::string & calcStrValue)34 void SetItemConstraintSizeSendParams(CalcDimension& doubleValue, std::string& calcStrValue)
35 {
36 if (doubleValue.Unit() == DimensionUnit::CALC) {
37 calcStrValue = doubleValue.CalcValue();
38 doubleValue.SetValue(DIMENSION_DEFAULT);
39 }
40 }
41 } // namespace
42
ResetColumnsTemplate(ArkUIRuntimeCallInfo * runtimeCallInfo)43 ArkUINativeModuleValue WaterFlowBridge::ResetColumnsTemplate(ArkUIRuntimeCallInfo* runtimeCallInfo)
44 {
45 EcmaVM* vm = runtimeCallInfo->GetVM();
46 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
47 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
48 void* nativeNode = nodeArg->ToNativePointer(vm)->Value();
49 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetColumnsTemplate(nativeNode);
50 return panda::JSValueRef::Undefined(vm);
51 }
52
SetColumnsTemplate(ArkUIRuntimeCallInfo * runtimeCallInfo)53 ArkUINativeModuleValue WaterFlowBridge::SetColumnsTemplate(ArkUIRuntimeCallInfo* runtimeCallInfo)
54 {
55 EcmaVM* vm = runtimeCallInfo->GetVM();
56 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
57 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
58 Local<JSValueRef> columnsTemplateArg = runtimeCallInfo->GetCallArgRef(NUM_1);
59 void* nativeNode = nodeArg->ToNativePointer(vm)->Value();
60 std::string columnsTemplateValue = columnsTemplateArg->ToString(vm)->ToString();
61 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().SetColumnsTemplate(nativeNode, columnsTemplateValue.c_str());
62 return panda::JSValueRef::Undefined(vm);
63 }
64
ResetRowsTemplate(ArkUIRuntimeCallInfo * runtimeCallInfo)65 ArkUINativeModuleValue WaterFlowBridge::ResetRowsTemplate(ArkUIRuntimeCallInfo* runtimeCallInfo)
66 {
67 EcmaVM* vm = runtimeCallInfo->GetVM();
68 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
69 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
70 void* nativeNode = nodeArg->ToNativePointer(vm)->Value();
71 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetRowsTemplate(nativeNode);
72 return panda::JSValueRef::Undefined(vm);
73 }
74
SetRowsTemplate(ArkUIRuntimeCallInfo * runtimeCallInfo)75 ArkUINativeModuleValue WaterFlowBridge::SetRowsTemplate(ArkUIRuntimeCallInfo* runtimeCallInfo)
76 {
77 EcmaVM* vm = runtimeCallInfo->GetVM();
78 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
79 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
80 Local<JSValueRef> rowsTemplateArg = runtimeCallInfo->GetCallArgRef(NUM_1);
81 void* nativeNode = nodeArg->ToNativePointer(vm)->Value();
82 std::string rowsTemplateValue = rowsTemplateArg->ToString(vm)->ToString();
83 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().SetRowsTemplate(nativeNode, rowsTemplateValue.c_str());
84 return panda::JSValueRef::Undefined(vm);
85 }
86
SetEnableScrollInteraction(ArkUIRuntimeCallInfo * runtimeCallInfo)87 ArkUINativeModuleValue WaterFlowBridge::SetEnableScrollInteraction(ArkUIRuntimeCallInfo* runtimeCallInfo)
88 {
89 EcmaVM* vm = runtimeCallInfo->GetVM();
90 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
91 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
92 Local<JSValueRef> enableScrollInteractionArg = runtimeCallInfo->GetCallArgRef(NUM_1);
93 void* nativeNode = nodeArg->ToNativePointer(vm)->Value();
94 if (enableScrollInteractionArg->IsUndefined() || !enableScrollInteractionArg->IsBoolean()) {
95 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().SetWaterFlowEnableScrollInteraction(nativeNode, true);
96 return panda::JSValueRef::Undefined(vm);
97 }
98 bool flag = enableScrollInteractionArg->ToBoolean(vm)->Value();
99 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().SetWaterFlowEnableScrollInteraction(nativeNode, flag);
100 return panda::JSValueRef::Undefined(vm);
101 }
102
ResetEnableScrollInteraction(ArkUIRuntimeCallInfo * runtimeCallInfo)103 ArkUINativeModuleValue WaterFlowBridge::ResetEnableScrollInteraction(ArkUIRuntimeCallInfo* runtimeCallInfo)
104 {
105 EcmaVM* vm = runtimeCallInfo->GetVM();
106 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
107 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
108 void* nativeNode = nodeArg->ToNativePointer(vm)->Value();
109 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetWaterFlowEnableScrollInteraction(nativeNode);
110 return panda::JSValueRef::Undefined(vm);
111 }
112
ResetColumnsGap(ArkUIRuntimeCallInfo * runtimeCallInfo)113 ArkUINativeModuleValue WaterFlowBridge::ResetColumnsGap(ArkUIRuntimeCallInfo* runtimeCallInfo)
114 {
115 EcmaVM* vm = runtimeCallInfo->GetVM();
116 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
117 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
118 void* nativeNode = nodeArg->ToNativePointer(vm)->Value();
119 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetColumnsGap(nativeNode);
120 return panda::JSValueRef::Undefined(vm);
121 }
122
SetColumnsGap(ArkUIRuntimeCallInfo * runtimeCallInfo)123 ArkUINativeModuleValue WaterFlowBridge::SetColumnsGap(ArkUIRuntimeCallInfo* runtimeCallInfo)
124 {
125 EcmaVM* vm = runtimeCallInfo->GetVM();
126 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
127 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
128 void* nativeNode = nodeArg->ToNativePointer(vm)->Value();
129 Local<JSValueRef> columnsGapArg = runtimeCallInfo->GetCallArgRef(NUM_1);
130
131 CalcDimension columnsGap;
132 std::string calcStr;
133 if (columnsGapArg->IsUndefined() || !ArkTSUtils::ParseJsDimensionVpNG(vm, columnsGapArg, columnsGap)) {
134 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetColumnsGap(nativeNode);
135 } else {
136 if (LessNotEqual(columnsGap.Value(), DIMENSION_DEFAULT)) {
137 columnsGap.SetValue(DIMENSION_DEFAULT);
138 }
139
140 if (columnsGap.Unit() == DimensionUnit::CALC) {
141 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().SetColumnsGap(
142 nativeNode, NUM_0, static_cast<int32_t>(columnsGap.Unit()), columnsGap.CalcValue().c_str());
143 } else {
144 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().SetColumnsGap(
145 nativeNode, columnsGap.Value(), static_cast<int32_t>(columnsGap.Unit()), calcStr.c_str());
146 }
147 }
148 return panda::JSValueRef::Undefined(vm);
149 }
150
ResetRowsGap(ArkUIRuntimeCallInfo * runtimeCallInfo)151 ArkUINativeModuleValue WaterFlowBridge::ResetRowsGap(ArkUIRuntimeCallInfo* runtimeCallInfo)
152 {
153 EcmaVM* vm = runtimeCallInfo->GetVM();
154 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
155 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
156 void* nativeNode = nodeArg->ToNativePointer(vm)->Value();
157 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetRowsGap(nativeNode);
158 return panda::JSValueRef::Undefined(vm);
159 }
160
SetRowsGap(ArkUIRuntimeCallInfo * runtimeCallInfo)161 ArkUINativeModuleValue WaterFlowBridge::SetRowsGap(ArkUIRuntimeCallInfo* runtimeCallInfo)
162 {
163 EcmaVM* vm = runtimeCallInfo->GetVM();
164 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
165 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
166 void* nativeNode = nodeArg->ToNativePointer(vm)->Value();
167 Local<JSValueRef> rowsGapArg = runtimeCallInfo->GetCallArgRef(NUM_1);
168
169 CalcDimension rowGap;
170 std::string calcStr;
171 if (rowsGapArg->IsUndefined() || !ArkTSUtils::ParseJsDimensionVpNG(vm, rowsGapArg, rowGap)) {
172 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetRowsGap(nativeNode);
173 } else {
174 if (LessNotEqual(rowGap.Value(), DIMENSION_DEFAULT)) {
175 rowGap.SetValue(DIMENSION_DEFAULT);
176 }
177 if (rowGap.Unit() == DimensionUnit::CALC) {
178 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().SetRowsGap(
179 nativeNode, 0, static_cast<int32_t>(rowGap.Unit()), rowGap.CalcValue().c_str());
180 } else {
181 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().SetRowsGap(
182 nativeNode, rowGap.Value(), static_cast<int32_t>(rowGap.Unit()), calcStr.c_str());
183 }
184 }
185 return panda::JSValueRef::Undefined(vm);
186 }
187
SetItemConstraintSize(ArkUIRuntimeCallInfo * runtimeCallInfo)188 ArkUINativeModuleValue WaterFlowBridge::SetItemConstraintSize(ArkUIRuntimeCallInfo* runtimeCallInfo)
189 {
190 EcmaVM* vm = runtimeCallInfo->GetVM();
191 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
192 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
193 void* nativeNode = nodeArg->ToNativePointer(vm)->Value();
194 Local<JSValueRef> minWidthValue = runtimeCallInfo->GetCallArgRef(NUM_1);
195 Local<JSValueRef> maxWidthValue = runtimeCallInfo->GetCallArgRef(NUM_2);
196 Local<JSValueRef> minHeightValue = runtimeCallInfo->GetCallArgRef(NUM_3);
197 Local<JSValueRef> maxHeightValue = runtimeCallInfo->GetCallArgRef(NUM_4);
198 CalcDimension minWidth;
199 CalcDimension maxWidth;
200 CalcDimension minHeight;
201 CalcDimension maxHeight;
202 std::string calcMinWidthStr;
203 std::string calcMaxWidthStr;
204 std::string calcMinHeightStr;
205 std::string calcMaxHeightStr;
206 if (minWidthValue->IsUndefined() || !ArkTSUtils::ParseJsDimensionVp(vm, minWidthValue, minWidth, false)) {
207 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetItemMinWidth(nativeNode);
208 } else {
209 SetItemConstraintSizeSendParams(minWidth, calcMinWidthStr);
210 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().SetItemMinWidth(
211 nativeNode, minWidth.Value(), static_cast<int32_t>(minWidth.Unit()), calcMinWidthStr.c_str());
212 }
213 if (maxWidthValue->IsUndefined() || !ArkTSUtils::ParseJsDimensionVp(vm, maxWidthValue, maxWidth, false)) {
214 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetItemMaxWidth(nativeNode);
215 } else {
216 SetItemConstraintSizeSendParams(maxWidth, calcMaxWidthStr);
217 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().SetItemMaxWidth(
218 nativeNode, maxWidth.Value(), static_cast<int32_t>(maxWidth.Unit()), calcMaxWidthStr.c_str());
219 }
220 if (minHeightValue->IsUndefined() || !ArkTSUtils::ParseJsDimensionVp(vm, minHeightValue, minHeight, false)) {
221 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetItemMinHeight(nativeNode);
222 } else {
223 SetItemConstraintSizeSendParams(minHeight, calcMinHeightStr);
224 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().SetItemMinHeight(
225 nativeNode, minHeight.Value(), static_cast<int32_t>(minHeight.Unit()), calcMinHeightStr.c_str());
226 }
227 if (maxHeightValue->IsUndefined() || !ArkTSUtils::ParseJsDimensionVp(vm, maxHeightValue, maxHeight, false)) {
228 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetItemMaxHeight(nativeNode);
229 } else {
230 SetItemConstraintSizeSendParams(maxHeight, calcMaxHeightStr);
231 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().SetItemMaxHeight(
232 nativeNode, maxHeight.Value(), static_cast<int32_t>(maxHeight.Unit()), calcMaxHeightStr.c_str());
233 }
234 return panda::JSValueRef::Undefined(vm);
235 }
236
ResetItemConstraintSize(ArkUIRuntimeCallInfo * runtimeCallInfo)237 ArkUINativeModuleValue WaterFlowBridge::ResetItemConstraintSize(ArkUIRuntimeCallInfo* runtimeCallInfo)
238 {
239 EcmaVM* vm = runtimeCallInfo->GetVM();
240 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
241 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
242 void* nativeNode = nodeArg->ToNativePointer(vm)->Value();
243 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetItemMaxHeight(nativeNode);
244 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetItemMaxWidth(nativeNode);
245 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetItemMinHeight(nativeNode);
246 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetItemMinWidth(nativeNode);
247 return panda::JSValueRef::Undefined(vm);
248 }
249
SetLayoutDirection(ArkUIRuntimeCallInfo * runtimeCallInfo)250 ArkUINativeModuleValue WaterFlowBridge::SetLayoutDirection(ArkUIRuntimeCallInfo* runtimeCallInfo)
251 {
252 EcmaVM* vm = runtimeCallInfo->GetVM();
253 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
254 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
255 Local<JSValueRef> layoutDirectionArg = runtimeCallInfo->GetCallArgRef(NUM_1);
256 void* nativeNode = nodeArg->ToNativePointer(vm)->Value();
257 auto value = static_cast<int32_t>(FlexDirection::COLUMN);
258 if (!layoutDirectionArg->IsUndefined()) {
259 ArkTSUtils::ParseJsInteger(vm, layoutDirectionArg, value);
260 }
261 if (value >= NUM_0 && value < static_cast<int32_t>(LAYOUT_DIRECTION.size())) {
262 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().SetLayoutDirection(nativeNode, value);
263 } else {
264 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetLayoutDirection(nativeNode);
265 }
266 return panda::JSValueRef::Undefined(vm);
267 }
268
ResetLayoutDirection(ArkUIRuntimeCallInfo * runtimeCallInfo)269 ArkUINativeModuleValue WaterFlowBridge::ResetLayoutDirection(ArkUIRuntimeCallInfo* runtimeCallInfo)
270 {
271 EcmaVM* vm = runtimeCallInfo->GetVM();
272 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
273 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
274 void* nativeNode = nodeArg->ToNativePointer(vm)->Value();
275 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetLayoutDirection(nativeNode);
276 return panda::JSValueRef::Undefined(vm);
277 }
278
SetNestedScroll(ArkUIRuntimeCallInfo * runtimeCallInfo)279 ArkUINativeModuleValue WaterFlowBridge::SetNestedScroll(ArkUIRuntimeCallInfo* runtimeCallInfo)
280 {
281 EcmaVM* vm = runtimeCallInfo->GetVM();
282 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
283 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
284 Local<JSValueRef> scrollForwardValue = runtimeCallInfo->GetCallArgRef(NUM_1);
285 Local<JSValueRef> scrollBackwardValue = runtimeCallInfo->GetCallArgRef(NUM_2);
286 void* nativeNode = nodeArg->ToNativePointer(vm)->Value();
287 int32_t froward = NUM_0;
288 int32_t backward = NUM_0;
289 ArkTSUtils::ParseJsInteger(vm, scrollForwardValue, froward);
290 if (froward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
291 froward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
292 froward = NUM_0;
293 }
294 ArkTSUtils::ParseJsInteger(vm, scrollBackwardValue, backward);
295 if (backward < static_cast<int32_t>(NestedScrollMode::SELF_ONLY) ||
296 backward > static_cast<int32_t>(NestedScrollMode::PARALLEL)) {
297 backward = NUM_0;
298 }
299 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().SetWaterFlowNestedScroll(nativeNode, froward, backward);
300 return panda::JSValueRef::Undefined(vm);
301 }
302
ResetNestedScroll(ArkUIRuntimeCallInfo * runtimeCallInfo)303 ArkUINativeModuleValue WaterFlowBridge::ResetNestedScroll(ArkUIRuntimeCallInfo* runtimeCallInfo)
304 {
305 EcmaVM* vm = runtimeCallInfo->GetVM();
306 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
307 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
308 void* nativeNode = nodeArg->ToNativePointer(vm)->Value();
309 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetWaterFlowNestedScroll(nativeNode);
310 return panda::JSValueRef::Undefined(vm);
311 }
312
SetFriction(ArkUIRuntimeCallInfo * runtimeCallInfo)313 ArkUINativeModuleValue WaterFlowBridge::SetFriction(ArkUIRuntimeCallInfo *runtimeCallInfo)
314 {
315 EcmaVM *vm = runtimeCallInfo->GetVM();
316 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
317 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
318 Local<JSValueRef> frictionArg = runtimeCallInfo->GetCallArgRef(NUM_1);
319 void *nativeNode = nodeArg->ToNativePointer(vm)->Value();
320 double friction = FRICTION_DEFAULT;
321 if (!ArkTSUtils::ParseJsDouble(vm, frictionArg, friction)) {
322 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetWaterFlowFriction(nativeNode);
323 } else {
324 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().SetWaterFlowFriction(nativeNode, friction);
325 }
326 return panda::JSValueRef::Undefined(vm);
327 }
328
ResetFriction(ArkUIRuntimeCallInfo * runtimeCallInfo)329 ArkUINativeModuleValue WaterFlowBridge::ResetFriction(ArkUIRuntimeCallInfo *runtimeCallInfo)
330 {
331 EcmaVM *vm = runtimeCallInfo->GetVM();
332 CHECK_NULL_RETURN(vm, panda::NativePointerRef::New(vm, nullptr));
333 Local<JSValueRef> nodeArg = runtimeCallInfo->GetCallArgRef(NUM_0);
334 void *nativeNode = nodeArg->ToNativePointer(vm)->Value();
335 GetArkUIInternalNodeAPI()->GetWaterFlowModifier().ResetWaterFlowFriction(nativeNode);
336 return panda::JSValueRef::Undefined(vm);
337 }
338 } // namespace OHOS::Ace::NG