Coverage Summary for Class: MacroGramsTextFieldKt (com.opty.numpad)

Class Method, % Branch, % Line, % Instruction, %
MacroGramsTextFieldKt 0% (0/10) 0% (0/76) 0% (0/87) 0% (0/1187)
MacroGramsTextFieldKt$MacroGramsTextField$10$1 0% (0/2) 0% (0/3) 0% (0/19)
MacroGramsTextFieldKt$MacroGramsTextField$4$1 0% (0/1) 0% (0/4) 0% (0/3) 0% (0/18)
Total 0% (0/13) 0% (0/80) 0% (0/93) 0% (0/1224)


 /*
  * Copyright 2024 Opty  All rights reserved.
  *
  * This software and associated documentation files (the "Software") are protected by copyright law and
  * international treaties. This software has been licensed to users under the terms of the End User
  * License Agreement (EULA) and Terms of Service (ToS) available within the application.
  *
  * Unauthorized copying, reproduction, modification, distribution, display, performance, sublicense, sale,
  * access or use of this Software, or any part thereof, is strictly prohibited and will be prosecuted
  * under applicable laws.
  *
  * The Software contains valuable trade secrets and proprietary information of Opty.
  * No part of the Software may be reproduced, distributed, or transmitted in any form or by any means,
  * including photocopying, recording, or other electronic or mechanical methods without the prior written
  * permission of Opty.
  *
  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
  * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  *
  * For licensing inquiries, please contact: support@optymacros.com
  */
 package com.opty.numpad
 
 import androidx.compose.foundation.gestures.detectTapGestures
 import androidx.compose.foundation.layout.Box
 import androidx.compose.foundation.layout.IntrinsicSize
 import androidx.compose.foundation.layout.PaddingValues
 import androidx.compose.foundation.layout.padding
 import androidx.compose.foundation.layout.width
 import androidx.compose.foundation.shape.RoundedCornerShape
 import androidx.compose.foundation.text.KeyboardActions
 import androidx.compose.foundation.text.KeyboardOptions
 import androidx.compose.runtime.Composable
 import androidx.compose.runtime.LaunchedEffect
 import androidx.compose.runtime.getValue
 import androidx.compose.runtime.mutableStateOf
 import androidx.compose.runtime.remember
 import androidx.compose.runtime.setValue
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.focus.FocusRequester
 import androidx.compose.ui.focus.focusRequester
 import androidx.compose.ui.focus.onFocusChanged
 import androidx.compose.ui.input.pointer.pointerInput
 import androidx.compose.ui.platform.LocalSoftwareKeyboardController
 import androidx.compose.ui.platform.testTag
 import androidx.compose.ui.text.TextRange
 import androidx.compose.ui.text.TextStyle
 import androidx.compose.ui.text.input.ImeAction
 import androidx.compose.ui.text.input.KeyboardType
 import androidx.compose.ui.text.input.TextFieldValue
 import androidx.compose.ui.text.style.TextAlign
 import androidx.compose.ui.unit.dp
 import com.opty.common.decimalSeparator
 import com.opty.designsystem.components.Text
 import com.opty.designsystem.components.textfield.TextField
 import com.opty.designsystem.components.textfield.expandWidthCentered
 import com.opty.designsystem.components.textfield.macroTextFieldColors
 
 private const val MINIMUM_WIDTH_QUANTITY_FIELD = 59f
 private const val CORNER_RADIUS = 25
 private const val TEXT_FIELD_START_PADDING = 10
 private const val TEXT_FIELD_VERTICAL_PADDING = 2
 private const val EXTRA_WIDTH_QUANTITY_FIELD = 2
 private const val TEXT_FIELD_MIN_HEIGHT = 28
 private val contentPaddingValues =
     PaddingValues(
         start = TEXT_FIELD_START_PADDING.dp,
         end = 0.dp,
         top = TEXT_FIELD_VERTICAL_PADDING.dp,
         bottom = TEXT_FIELD_VERTICAL_PADDING.dp,
     )
 
 @Composable
 fun MacroGramsTextField(
     textFieldStartG: Int,
     setValue: (Float) -> Unit,
     textStyle: TextStyle,
     enabled: Boolean,
     modifier: Modifier = Modifier,
     testTag: String? = null,
     readOnly: Boolean = false,
     onDoneClick: () -> Unit = {},
     onFocusGain: () -> Unit = {},
     onClickWhenDisable: () -> Unit = {},
     suffixRightPadding: Float = 0f,
     suffixLeftPadding: Float = 0f,
     minWidth: Float = MINIMUM_WIDTH_QUANTITY_FIELD,
     trailingIcon: @Composable (() -> Unit)? = null,
 ) {
     var waitingFirstFocus by remember { mutableStateOf(false) }
     val keyboardController = LocalSoftwareKeyboardController.current
     val focusRequester = remember { FocusRequester() }
     val textFieldValue = TextFieldValue(textFieldStartG.toString(), TextRange(textFieldStartG.toString().length))
     val numpadController = LocalNumpadController.current
     val numpadOwner = remember { Any() }
     val useEmbeddedNumpad = numpadController != null && enabled && !readOnly
 
     LaunchedEffect(waitingFirstFocus, enabled) {
         if (enabled && waitingFirstFocus) {
             focusRequester.requestFocus()
             waitingFirstFocus = false
         }
     }
 
     TextField(
         readOnly = readOnly || useEmbeddedNumpad,
         contentPaddingValues = contentPaddingValues,
         trailingIcon = trailingIcon,
         value = textFieldValue,
         onValueChange = { newTextFieldValue ->
             onGramsValueChange(newTextFieldValue, setValue)
         },
         textStyle = remember(textStyle) { textStyle.copy(textAlign = TextAlign.Center) },
         keyboardOptions =
             remember(useEmbeddedNumpad) {
                 KeyboardOptions(
                     keyboardType = KeyboardType.Number,
                     imeAction = ImeAction.Done,
                     showKeyboardOnFocus = !useEmbeddedNumpad,
                 )
             },
         keyboardActions =
             remember(onDoneClick) {
                 KeyboardActions(
                     onDone = {
                         keyboardController?.hide()
                         onDoneClick()
                     },
                 )
             },
         suffix = {
             Box(modifier = Modifier.padding(end = suffixRightPadding.dp, start = suffixLeftPadding.dp)) {
                 Text("g", style = textStyle)
             }
         },
         modifier =
             modifier
                 .focusRequester(focusRequester)
                 .width(IntrinsicSize.Min)
                 .onFocusChanged { focusState ->
                     if (focusState.isFocused) {
                         onFocusGain()
                         if (useEmbeddedNumpad) {
                             keyboardController?.hide()
                             numpadController.show(
                                 owner = numpadOwner,
                                 value = textFieldValue,
                                 spec =
                                     NumpadInputSpec(
                                         maxValue = Int.MAX_VALUE.toDouble(),
                                         decimalSeparator = decimalSeparator,
                                         maxFractionDigits = 0,
                                     ),
                                 onValueChange = { onGramsValueChange(it, setValue) },
                                 onDone = onDoneClick,
                             )
                         }
                     } else {
                         numpadController?.dismiss(numpadOwner)
                     }
                 }.expandWidthCentered(EXTRA_WIDTH_QUANTITY_FIELD, minWidth, true)
                 .let { textFieldModifier ->
                     if (testTag != null) {
                         textFieldModifier.testTag(testTag)
                     } else {
                         textFieldModifier
                     }
                 }
                 .pointerInput(Unit) {
                     detectTapGestures(
                         onTap = {
                             waitingFirstFocus = true
                             onClickWhenDisable()
                         },
                     )
                 },
         shape = RoundedCornerShape(CORNER_RADIUS.dp),
         minHeight = TEXT_FIELD_MIN_HEIGHT.dp,
         colors = macroTextFieldColors(),
         singleLine = true,
         enabled = enabled,
     )
 }
 
 private fun onGramsValueChange(
     newTextFieldValue: TextFieldValue,
     setValue: (Float) -> Unit,
 ) {
     val safeString =
         buildString {
             append("0")
             newTextFieldValue.text.forEach { append(if (it.isDigit()) it else "") }
         }
     safeString.toIntOrNull()?.let {
         setValue(it.toFloat())
     }
 }