TextField with inputmask, how to check if is empty? - qt

How do I check is a Textfield is empty, when I use an inputmask on it?
Ex: I have a textfield with this mask: "099.999.999-90"
When I get the textfield.text, I get this: "..-".
This is making it impossible to do this comparison:
if( textfield.text === "") {}
I tried compare to textfield.inputmask, but that doesn't work either, because the value of textfield.inputmask is: "099.999.999-90"
and this comparison:
if( textfield.text === textfield.inputmask) {}
returns false.
Is there some solution for this trouble?

If you assigned input mask to TextField, it would be better to check for acceptableInput property. If acceptableInput is false, then TextField contents doesn't conform to input mask, so it can be considered as "empty" as well.

Related

TextField is hiding under the keyboard when focused

I have a problem with TextField that is hiding under the keyboard, I've found the answers for the similar question here, but they are not helping me in my case. I have a LazyColumn with the list of different composables in it, and when I have not enough elements in the window for scroll to be activated, focusing on TextField is not lifting up focused TextField above keyboard. Keyboard just hides it.
My code:
val listState = rememberLazyListState()
typealias ComposableType = #Composable (Int) -> Unit
val uiList = listOf<ComposableType>( {IconButton}, {Text}, {CustomTextField(listState,it)}, {CustomTextField(listState,it)})
LazyColumn() {
itemsIndexed(uiList) { index, ui ->
ui.invoke(index)
}
}
val scope = rememberCoroutineScope()
#Composable
CustomTextField(scrollState: LazyListState, position: Int) {
OutlinedTextField(
modifier = Modifier.onFocusEvent { focusState ->
if (focusState.isFocused) {
scope.launch {
scrollState.animateScrollToItem(position)
}
}
)
}
So, for example if i have 10 CustomTextFields in my uiList, scroll works when one of TextField is focused. But when there are only 2 TextFields in the uiList, focusing on either of them does not lift up them above keyboard.
Also I tried using RelocationRequester() and used Column with scroll instead of LazyColumn, none of it helped.
It's a combination of things...
You need to add this in your activity's declaration in Android.xml
android:windowSoftInputMode="adjustResize"
Use BringIntoViewRequester as modifier in your TextField as you mentioned.
.bringIntoViewRequester(yourBringIntoViewRequester)
The steps above worked for me when the component gain focus programatically (using FocusRequester). However, when the user taps on the TextField, it didn't work for me. So I implemented a workaround (which I'm not very proud of): when the TextField gain focus, I wait a bit to use the RelocationRequester. So I added this modifier to my TextField.
.onFocusEvent {
if (it.isFocused) {
coroutineScope.launch {
delay(200)
yourBringIntoViewRequester.bringIntoView()
}
}
}
These three things worked for me.
You need to add this in your activity's declaration in AndroidManifest.xml
android:windowSoftInputMode="adjustResize"
You have to set wrapContentHeight().navigationBarsWithImePadding() to the modifier of parent composable
Column(modifier = Modifier.wrapContentHeight().navigationBarsWithImePadding()){}
This solution worked for me.
Add this to your activity in manifest file
android:windowSoftInputMode="adjustResize"
And in the column for example use :
Column (horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.fillMaxWidth()
.wrapContentHeight()
.navigationBarsPadding().imePadding()
.verticalScroll(rememberScrollState())
.fillMaxHeight()
.padding(top = 20.dp))
works for me , happy coding ..

Why Qt QML doesn't properly bind properties?

It's very weird because I use almost the same pattern in another QML file and it works correctly! I'm sure I'm not reassigning value anywhere (the only thing I know that can make property static)!
I have something like this (it's a simplified example and probably works as expected):
Item {
property bool isExpanded: false
MouseArea {
anchors.fill: parent
onClicked: {
isExpanded = !isExpanded
console.log(isExpanded)
console.log(myId.visible)
}
}
MyCustomItem {
id: myId
visible: isExpanded
// other stuff
}
}
IsExpanded changes after I click but Item visibility always stays the same! And I have many other properties for my item (for example height: isExpanded ? someval : 0) which doesn't change too! It sort of works if I always change everything manually but whats the point then? And in another QML I use a similar pattern and there it works!
BUT! If I put, for example onDoubleClicked/another button press:
myId.visible = Qt.binding(function() {return isExpanded})
it works as it should! So, for some unknown reason it doesn't 'bind' them when it should in regular property declaration (visible: isExpanded).
So the question is, do I really need to explicitly tell Qt to bind property to make it work?
Edit: To make clear: I'm certain I don't reassign visible property anywhere. I doble checked it. And though qml size is only about 100 of lines I used ctrl+f to find any mention of visible and found none except already mentioned.
If there is more reliable way to tell what's from/ check for possible reassignments somewhere or something, please tell.
So the question is, do I really need to explicitly tell Qt to bind property to make it work?
Yes.
When declaring a property, you can use the binding syntax e.g. visible: isExpanded. But setting a property in imperative code using normal JavaScript syntax (i.e. the assignment operator) will break any existing binding and overwrite the property's value. If you want to explicitly set a property binding in imperative code, use the Qt.binding() method (docs).
Although your actual problem is due to falling victim to QML's scoping rules. Your MyCustomItem type has an isExpanded property, so when you declare:
MyCustomItem {
id: myId
visible: isExpanded
// other stuff
}
You're actually binding visible to MyCustomItem::isExpanded. So to fix just be explicit as to which isExpanded you are referring to:
Item {
id: base
property bool isExpanded: false
MouseArea {
anchors.fill: parent
onClicked: {
isExpanded = !isExpanded
console.log(isExpanded)
console.log(myId.visible)
}
}
MyCustomItem {
id: myId
visible: base.isExpanded
// other stuff
}
}
I found the issue! Very unobvious (haven't found documented anywhere) and it fails silently.
The problem is that MyCustomItem contained a property with the same name of outer booleal property isExpanded so in this code:
MyCustomItem {
id: myId
visible: isExpanded
// other stuff
}
isExpanded is from MyCustomItem. But without any indication of that. Well, now I know that this could happen and would probably spot it if it happens again but this is really counter-intuitive. What if it wasn't my item but someone else's and I didn't know that it had such property? Qt creator could've at least warned about possible ambiguities.

applyCss doesn't work when remove a styleClass

I have a TextField styled by changing styleClass when its value is valid/invalid but there is a great problem. When the value becomes valid I still should change the TextField once more to make the styleClass be applied. I tried with applyCss() method just after, but it didn't work :(.
if(!valid){
field.getStyleClass().add("invalid-field");//Works excellent
} else {
field.getStyleClass().remove("invalid-field");//Doesn't work up to the time textProperty doesn't change once again
field.applyCss();
One common cause of these bugs is that you may have added the "invalid-field" style class more than once. (Remember, getStyleClass() returns a List<String>, not a Set<String>.) So you should probably take steps to make sure the style class is only added once, or take steps to remove all occurrences when you remove it.
I like to do both (where I come from, we call this a "belt and braces approach").
ObservableList<String> styleClasses = field.getStyleClass();
if(!valid)
if( ! styleClass.contains("invalid-field")){
styleClass.add("invalid-field");
}
} else {
// remove all occurrences:
styleClass.removeAll(Collections.singleton("invalid-field"));
}

flex mobile validation: how to remove red error border?

I use validators in my flex mobile application.
I want to remove the red border when validator has triggered an error.
<mx:EmailValidator id="emailV" source="{login_txt}" property="text" triggerEvent="click" trigger="{connexion_btn}" />
<mx:StringValidator id="passwordV" source="{password_txt}" property="text" trigger="{connexion_btn}" triggerEvent="click" />
I tried:
target.errorString = null; // not good
Any clue ?
Usually I would set the errorString to an empty string; and I do that on the instance of the component with the red string on it. I believe in that case, it would be your trigger component:
login_txt.errorString = '';
password_txt.errorString = '';
I'm unclear based on the limited code provided if the target you are setting the errorString on will be the same as the actual component specified as the validator source. It could be, we just aren't provided enough information to know for sure.
The red glow is defined in spark.skins.spark.ErrorSkin, which is the default value of a UIComponent's errorSkin property. You can't set this property to null, but you can extend the ErrorSkin class and override the methods that generate the glow (namely, updateDisplayList and processBitmap).
I created a NullFocusSkin that I use to remove the red error glow and the blue focus glow. I set the component's errorSkin and focusSkin properties to that and hey presto - no more nasty glow, and no need to manually clear the errorString!
import spark.skins.spark.HighlightBitmapCaptureSkin;
public class NullFocusSkin extends HighlightBitmapCaptureSkin
{
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
// Do nothing.
}
override protected function processBitmap():void
{
// Do nothing.
}
}
Besides setting the error string to empty, I had to call showFocus(), or the red border wouldn't go away.
login.errorString = '';
login.focusManager.showFocus();

Styling UITextField

I've been playing around with different methods of determining at runtime the width of a "label" so that I can resize the "label" because I don't want it to truncate. I've finally found an easy solution through UITextField which allows me to set the .autoSize which is great! However, now I'm trying to "style" (simply adjust font and font size) of the UITextField but it seems that I have to do it manually with '.htmlText' (which I'll gladly accept if that is the ONLY way).
I'm using the .text to set the value of the label.
My test case involves a HBox (I'm actually using a Grid but they should be the same and I've done testing on both):
I style the HBox and the style carries through to the UITextField. I don't believe this will work for me because I have other components inside that I need to style differently.
I've tried: UITextFormat and TextFormat (I see that the .htmlText being updated accordingly but the output doesn't update. Then I noticed that whenever I called hbox.addChild(myUITextField) it would override the .htmlText
I've tried setting the style with myUITextField.setStyle("fontSize", 20) before and/or after the call to addChild neither of which made an impact on the display as per what I noted above.
Changes are being made but they seem to be overrided when I add it to the display.
So what do I need to do in order to style the UITextField aside from manually setting it along with my contents in .htmlText? Solutions not using UITextField is fine as long as there is some easy way of not truncating the text.
EDIT: I want to just do textField.setStyle('fontSize', 20) and expect that every time I change the text, I wouldn't need to use HTML to go with it (so I can just do textField.text = 'something else' and expect that it will still have a font size of 20). This is what I meant by not using .htmlText (sorry if I wasn't clear before).
2nd EDIT: I guess I should present the whole issue and maybe that'll clarify what I did wrong or couldn't achieve.
My intent is to have a Grid and add text into it. I do not want it to wrap or scroll so I add it to the next row in the Grid when the current row's children total width exceeds some number. In order to add it to the next row, I need to be able to calculate the width of the text. I would like to be able to style that text individually based on cases and there might be other components (like a TextInput). Essentially what I'm trying to accomplish is "Fill in the Blank".
I've included code to show what I'm currently doing and it works somewhat. It might be un-related to the original issue of styling but I can't figure out how to adjust the distance between each UITextField but aside from that this fits what I would like to accomplish. Relevant to the question is: I would like to change the way I style each UITextField (currently setting .htmlText) into something a bit straightforward though like I previously mentioned I'll gladly accept using .htmlText if that's the only solution.
So I have a Grid with x Rows in it and in each row, I have exactly one GridItem. Based on the input, I add UITextField and TextInput into the GridItem going on to the next GridItem when necessary. If you have a better way of doing so then that would be better but I guess what I really want is to find a different way of styling.
Also another problem, I'm not sure of the exact way to add a TextField into the display. I tried:
var t : TextField = new TextField();
t.text = "I'm a TextField";
hBox.addChild(t); // doesn't work
//this.addChild(t); // doesn't work either
But I get the following error:
TypeError: Error #1034: Type Coercion failed: cannot convert flash.text::TextField#172c8f9 to mx.core.IUIComponent.
Here's what I have that's working.
private function styleQuestionString(str : String) : String {
return '<FONT leading="1" face="verdana" size="20">' + str + '</FONT>';
}
private function loadQuestion(str : String) : void {
/* Split the string */
var tmp : Array = str.split("_");
/* Track the current width of the GridItem */
var curWidth : int = 0;
/* Display components that we will add */
var txtField : UITextField = null;
var txtInput : TextInput = null;
/* Track the current GridItem */
var curGridItem : GridItem = null;
/* Track the GridItem we can use */
var gridItemAC : ArrayCollection = new ArrayCollection();
var i : int = 0;
/* Grab the first GridItem from each GridRow of Grid */
var tmpChildArray : Array = questionGrid.getChildren();
for (i = 0; i < tmpChildArray.length; i++) {
gridItemAC.addItem((tmpChildArray[i] as GridRow).getChildAt(0));
}
curGridItem = gridItemAC[0];
gridItemAC.removeItemAt(0);
/* Used to set the tab index of the TextInput */
var txtInputCounter : int = 1;
var txtFieldFormat : UITextFormat = new UITextFormat(this.systemManager);
txtFieldFormat.leading = "1";
//var txtFieldFormat : TextFormat = new TextFormat();
//txtFieldFormat.size = 20;
/* Proper Order
txtField = new UITextField();
txtField.text = tmp[curItem];
txtField.autoSize = TextFieldAutoSize.LEFT;
txtField.setTextFormat(txtFieldFormat);
*/
var txtLineMetrics : TextLineMetrics = null;
var tmpArray : Array = null;
curGridItem.setStyle("leading", "1");
var displayObj : DisplayObject = null;
for (var curItem : int= 0; curItem < tmp.length; curItem++) {
/* Using UITextField because it can be auto-sized! */
/** CORRECT BLOCK (ver 1)
txtField = new UITextField();
txtField.text = tmp[curItem];
txtField.autoSize = TextFieldAutoSize.LEFT;
txtField.setTextFormat(txtFieldFormat);
***/
tmpArray = (tmp[curItem] as String).split(" ");
for (i = 0; i < tmpArray.length; i++) {
if (tmpArray[i] as String != "") {
txtField = new UITextField();
txtField.htmlText = styleQuestionString(tmpArray[i] as String);
//txtField.setTextFormat(txtFieldFormat); // No impact on output
txtLineMetrics = curGridItem.measureHTMLText(txtField.htmlText);
curWidth += txtLineMetrics.width + 2;
if (curWidth >= 670) {
curGridItem = gridItemAC[0];
curGridItem.setStyle("leading", "1");
if (gridItemAC.length != 1) {
gridItemAC.removeItemAt(0);
}
// TODO Configure the proper gap distance
curWidth = txtLineMetrics.width + 2;
}
displayObj = curGridItem.addChild(txtField);
}
}
//txtField.setColor(0xFF0000); // WORKS
if (curItem != tmp.length - 1) {
txtInput = new TextInput();
txtInput.tabIndex = txtInputCounter;
txtInput.setStyle("fontSize", 12);
txtInputCounter++;
txtInput.setStyle("textAlign", "center");
txtInput.width = TEXT_INPUT_WIDTH;
curWidth += TEXT_INPUT_WIDTH;
if (curWidth >= 670) {
curGridItem = gridItemAC[0];
if (gridItemAC.length != 1) {
gridItemAC.removeItemAt(0);
}
// TODO Decide if we need to add a buffer
curWidth = TEXT_INPUT_WIDTH + 2;
}
curGridItem.addChild(txtInput);
txtInputAC.addItem(txtInput);
/* Adds event listener so that we can perform dragging into the TextInput */
txtInput.addEventListener(DragEvent.DRAG_ENTER, dragEnterHandler);
txtInput.addEventListener(DragEvent.DRAG_DROP, dragDropHandler);
txtInput.addEventListener(DragEvent.DRAG_EXIT, dragExitHandler);
}
/* Add event so that this label can be dragged */
//txtField.addEventListener(MouseEvent.MOUSE_MOVE, dragThisLabel(event, txtField.text));
}
}
After about 8 hours of searching for a solution to what would seem to be such a simple issue I FINALLY stumbled on your posts here... Thankyou!!!
I have been stumbling around trying to get TextField to work and had no Joy, Label was fine, but limited formatting, and I need to be able to use embedded fonts and rotate. After reading the above this finally worked for me:
var myFormat:TextFormat = new TextFormat();
myFormat.align = "center";
myFormat.font = "myFont";
myFormat.size = 14;
myFormat.color = 0xFFFFFF;
var newTxt:UITextField = new UITextField();
newTxt.text = "HELLO";
addChild(newTxt);
newTxt.validateNow();
newTxt.setTextFormat(myFormat);
The order of addChild before the final 2 steps was critical! (myFont is an embedded font I am using).
One again... a thousand thankyou's...
John
EDIT BASED ON THE ASKERS FEEDBACK:
I didn't realize you wanted to just apply one style to the whole textfield, I thought you wanted to style individual parts. This is even simpler for you, won't give you any trouble at all :)
var textFormat: TextFormat = new TextFormat("Arial", 12, 0xFF0000);
myText.setTextFormat(textFormat);
Be aware that this sets the style to the text that is in the TextField, not necessarily future text you put in. So have your text in the field before you call setTextFormat, and set it again every time you change it just to be sure it stays.
It's probably best if you use a normal TextField as opposed to the component. If you still want the component you may need to call textArea.validateNow() to get it to update with the new style (not 100% sure on that one though) Adobe components are notoriously bad, and should be avoided. :(
To see all available options on the TextFormat object see here
END EDIT ---------
This is easy enough to just do with CSS in a normal old TextField.
var myCSS: String = "Have some CSS here, probably from a loaded file";
var myHTML: String = "Have your HTML text here, and have it use the CSS styles";
// assuming your textfield's name is myText
var styleSheet: StyleSheet = new StyleSheet();
styleSheet.parseCSS(myCSS);
myText.autoSize = TextFieldAutoSize.LEFT;
myText.styleSheet = styleSheet;
myText.htmlText = myHTML;
Supported HTML tags can be found here
Supported CSS can be found here
The reason you have a problem adding Textfield to containers is that it doesn't implement the IUIComponent interface. You need to use UITextField if you want to add it. However, that's presenting me with my own styling issues that brought me to this question.
A few things I know:
TextField is styled using the TextFormat definition, and applying it to the textfield. As Bryan said, order matters.
setStyle does nothing on IUITextField, and the TextFormat method doesn't seem to work the same as in normal TextFields. (Edit #2: Ahah. You need to override the "validateNow" function on UITextFields to use the setTextFormat function)
To autosize a TextArea, you need to do something like this (inheriting from TextArea):
import mx.core.mx_internal;
use namespace mx_internal;
...
super.mx_internal::getTextField().autoSize = TextFieldAutoSize.LEFT;
this.height = super.mx_internal::getTextField().height;
Found this code on, I think, on StackOverflow a while back. Apologies to the original author. But the idea is that you need to access the "mx_internal" raw textfield.
Text and TextArea have wrapping options. (Label does not). So if you set the explicit width of a Text object, you might be able to size using the measuredHeight option and avoid truncation.
(edit: That was #4, but stackoverflow parsed it into a 1...)

Resources