I grappled with this for a while. Seems to be a semi bug.
If you add a leftButton or a rightButton to a textField like so:
var leftButton = Ti.UI.createButton({
image: 'someImage.png'
})
var textField = Ti.UI.createTextField({
leftButton: leftButton,
leftButtonMode: Ti.UI.INPUT_BUTTONMODE_ALWAYS,
leftButtonPadding: 100
})
...then you won't get to see your button. Why?
There might be 2 issues with this code.
1- Check image path you assigning to button .. ? (Height,width)
for test purpose try use any system button and see if its appear or not .?
var leftButton = Titanium.UI.createButton({
style:Titanium.UI.iPhone.SystemButton.DISCLOSURE
});
2- second issue might be with padding of left button
try to use it without padding and then see what happens.
var win = Titanium.UI.createWindow({
title:"Configuring text field and text area keyboard types",
backgroundColor:"#347AA9",
exitOnClose:true
});
//These buttons will appear within the text field
var clearButton = Titanium.UI.createButton({
title:"Clear",
height:24,
width:52
});
var submitButton = Titanium.UI.createButton({
title:"Submit",
height:24,
width:60
});
var textField = Titanium.UI.createTextField({
top:"25%",
height:35,
width:600,
backgroundColor:"#ffffff",
borderStyle:Titanium.UI.INPUT_BORDERSTYLE_ROUNDED,
hintText:"Type something",
keyboardType:Titanium.UI.KEYBOARD_DEFAULT,
leftButton:clearButton,
rightButton:submitButton
});
clearButton.addEventListener("click", function(e){
//Clear the value of the text field
textField.value = "";
});
submitButton.addEventListener("click", function(e){
//Pretend to submit the value of the text field
//Be sure that you've typed something in!
if(textField.value != ""){
alert(textField.value);
}else{
alert("Enter some text");
}
});
//Add an event listener to the window that allows for the keyboard or input keys to be hidden if the user taps outside a text field
//Note: each text field to be blurred would be added below
win.addEventListener("click", function(e){
textField.blur(); // Cause the text field to lose focus, thereby hiding the keyboard (if visible)
});
win.add(textField);
win.open();
The problem is in the leftButtonMode property. Give it any value at all, and the button won't show. If you don't use this property, the button will show just fine.
The padding property is not a problem for leftButton. But if you use it on a rightButton, it will likely throw your button outside the screen. I've tried a negative value too, without success.
Be aware too that the leftButton and rightButton options don't work on Android.
Related
In iOS 14 I have configured a button to display a menu of questions. On its own the button is working perfectly. Here’s how it’s configured:
let button = UIButton()
button.setImage(UIImage(systemName: "chevron.down"), for: .normal)
button.showsMenuAsPrimaryAction = true
button.menu = self.menu
I have it set on a text field’s rightView. This is how it looks when the menu is displayed.
When a menu item is tapped, that question appears in a label just above the text field. It works great. Except….
I also want the menu to appear when the user taps in the text field. I don’t want them to have to tap on the button specifically. In the old days of target-action this was easy: button.sendActions(for: .touchUpInside). I tried that and .menuActionTriggered and .primaryActionTriggered but none of those work. I think sendActions() is looking for the old selector based actions.
But there’s a new sendActions(for: UIControl.Event) method. Here’s what I’m trying in textFieldShouldBeginEditing():
func textFieldShouldBeginEditing(_ textField: UITextField) -> Bool {
let tempAction = UIAction { action in
print("sent action")
}
menuButton.sendAction(tempAction)
print("textField tapped")
return false
}
Sure enough, “sent action” and “textField tapped” appear in the console when the text field is tapped. But I have no idea what UIAction I could send that means “display your menu”. I wish there was this method: send(_: UIControl.Event).
Any ideas on how to get the button to display its menu when the text field is tapped?
ps. yes, textFieldShouldBeginEditing will need to know if a question has been selected and in that case will need to allow editing. That part is easy.
From what I read, you cannot programmatically trigger UIContextMenuInteraction as interactions seem to be handled internally by itself unlike send(_: UIControl.Event)
I see this mentioned on this SO post here
Also in the docs it seems that we don't have access to interaction management Apple needs to decide 3D touch is available or default back to long tap
From the docs
A context menu interaction object tracks Force Touch gestures on devices that support 3D Touch, and long-press gestures on devices that don't support it.
Workaround
I can propose the following workaround for your use case. My example was created using frame instead of auto layout as faster and easier to demo the concept this way, however you will need to make adjustments using autolayout
1. Create the UI elements
// I assume you have a question label, answer text field and drop down button
// Set up should be adjusted in case of autolayout
let questionLabel = UILabel(frame: CGRect(x: 10, y: 100, width: 250, height: 20))
let answerTextField = UITextField(frame: CGRect(x: 10, y: 130, width: 250, height: 50))
let dropDownButton = UIButton()
2. Regular setup of the label and the text view first
// Just regular set up
private func configureQuestionLabel()
{
questionLabel.textColor = .white
view.addSubview(questionLabel)
}
// Just regular set up
private func configureAnswerTextField()
{
let placeholderAttributes = [NSAttributedString.Key.foregroundColor :
UIColor.lightGray]
answerTextField.backgroundColor = .white
answerTextField.attributedPlaceholder = NSAttributedString(string: "Tap to select a question",
attributes: placeholderAttributes)
answerTextField.textColor = .black
view.addSubview(answerTextField)
}
3. Add the button to the text view
// Here we have something interesting
private func configureDropDownButton()
{
// Regular set up
dropDownButton.setImage(UIImage(systemName: "chevron.down"), for: .normal)
dropDownButton.showsMenuAsPrimaryAction = true
// 1. Create the menu options
// 2. When an option is selected update the question label
// 3. When an option is selected, update the button frame
// Update button width function explained later
dropDownButton.menu = UIMenu(title: "Select a question", children: [
UIAction(title: "Favorite movie") { [weak self] action in
self?.questionLabel.text = action.title
self?.answerTextField.placeholder = "Add your answer"
self?.answerTextField.text = ""
self?.updateButtonWidthIfRequired()
},
UIAction(title: "Car make") { [weak self] action in
self?.questionLabel.text = action.title
self?.answerTextField.placeholder = "Add your answer"
self?.answerTextField.text = ""
self?.updateButtonWidthIfRequired()
},
])
// I right align the content and set some insets to get some padding from
// the right of the text field
dropDownButton.contentHorizontalAlignment = .right
dropDownButton.contentEdgeInsets = UIEdgeInsets(top: 0.0,
left: 0.0,
bottom: 0.0,
right: 20.0)
// The button initially will stretch across the whole text field hence we
// right aligned the content above
dropDownButton.frame = answerTextField.bounds
answerTextField.addSubview(dropDownButton)
}
// Update the button width if a menu option was selected or not
func updateButtonWidthIfRequired()
{
// Button takes the text field's width if the question isn't selected
if let questionText = questionLabel.text, questionText.isEmpty
{
dropDownButton.frame = answerTextField.bounds
return
}
// Reduce button width to right edge as a question was selected
dropDownButton.frame = CGRect(x: answerTextField.frame.width - 50.0,
y: answerTextField.bounds.origin.y,
width: 50,
height: answerTextField.frame.height)
}
4. End Result
Start with a similar view to yours
Then I tap in the middle of the text field
It displays the menu as intended
After selecting an option, the question shows in the label and the placeholder updates
Now I can start typing my answer using the text field and the button is only active on the right side since it was resized
And the button is still active
Final thoughts
Could be better to put this into a UIView / UITextField subclass
This was an example using frames with random values, adjustments need to made for autolayout
Edits from OP (too long for a comment):
I had tried setting contentEdgeInsets so the button was way over to the left but it covered up the placeholder text.
Your idea of simply adding the button as a subview of the text field was the key, but...
After selecting a question and resizing the button, if the text field was the first responder, tapping the button had no effect. The button was in the view hierarchy but the text field got the tap.
So, when a question is selected, I remove the button from its superview (the textfield) and add it to the textField's rightView. Then it would accept a tap even if the textField was the first responder.
And, as you suspected, I had to pin the button to the textField with constraints.
After selecting the whole text in a TextField using TextSelection() it does indeed select the whole text but after pressing a key on the keyboard, it starts adding pressed letters/numbers to the start of the text as opposed to deleting the old one and replacing it with the newly typed letters/numbers.
Is this expected behavior? If so, is there any way I can programatically select the text and then replace it upon pressing a key on the keyboard?
This is how I select the text:
manualEditorNode.addListener(() {
if (manualEditorNode.hasFocus) {
manualInputController.selection = TextSelection(
baseOffset: 0, extentOffset: manualInputController.text.length);
}
});
The following works for me in my program. Maybe you can try something like this?
var cursorPos = textInputController.selection;
setState(() {
textInputController.text = newInput;
if (cursorPos.start > newInput.length) {
cursorPos = new TextSelection.fromPosition(
new TextPosition(offset: newInput.length));
}
textInputController.selection = cursorPos;
});
With the coming of multi-process Firefox, I have decided to revamp my addon. It is a toolbar addon that was built on XUL. Now I want to build it using the Addon SDK.
The old XUL overlay allowed for onMouseOver events for buttons. But the new addon SDK only has the one listener for click.
How can I get an onMouseOver (Hover) event for a toolbar button?
Maybe there is some way to add css (:hover) to the button element?
I found this, and am working on getting it in order, but maybe there's a better way?
Here is what I have so far:
var {Cu, Cc, Ci} = require("chrome");
Cu.import('resource://gre/modules/Services.jsm');
var aDOMWindow = Services.wm.getMostRecentWindow('navigator:browser');
aDOMWindow.addEventListener('mouseover', onSpatMouseover, true);
function onMyMouseover(event){
if (event.target.nodeName == 'toolbarbutton'){
console.log(event.explicitOriginalTarget.nodeName);
if(event.currentTarget.nodeName == '#MyButton'){
console.log("found the button");
}
}
}
But it does not yet find #MyButton.
First of all, error message you're getting already tells you how to make it work.
But it's not necessarily what you need anyway, usually sdk/view/core provides access to the underlying XUL elements through one of its 3 methods.
Here is a complete example of how to do this. There are two functions, actually, one for mouseover and one for mouseout. If you change the icon of a button using mouseover, you need mouseout to change it back to normal.
const { browserWindows } = require("sdk/windows");
const { CustomizableUI } = require('resource:///modules/CustomizableUI.jsm');
const { viewFor } = require("sdk/view/core");
const { ActionButton } = require("sdk/ui/button/action");
var myButton = ActionButton({
id: "mybutton",
label: "My Button",
icon: { "16": "./icon-16.png", "32":"./icon-32.png", "64": "./icon-64.png" },
onClick: function(state) {
console.log("My Button was clicked");
}
});
//create a mouseover effect for a control
exports.MouseOver = (whatbutton, whatwindow, whatfunction) =>{
CustomizableUI.getWidget( viewFor(whatbutton).id ).forWindow(whatwindow).node.addEventListener('mouseover', whatfunction, true);
};
exports.MouseOut = (whatbutton, whatwindow, whatfunction) =>{
CustomizableUI.getWidget( viewFor(whatbutton).id ).forWindow(whatwindow).node.addEventListener('mouseout', whatfunction, true);
};
function myMouseOverFunction(){
console.log("mousing over...");
}
function myMouseOutFunction(){
console.log("mousing out...");
}
//add events to the browser window
for(let w of browserWindows){
exports.MouseOver(mybutton, viewFor(w), myMouseOverFunction);
exports.MouseOut(mybutton, viewFor(w), onMouseOutFunction );
}
I wanted to add new button to the dialog, with out loosing the previous buttons.
I had used the following code which didn't work ....
menu.dialog("open");
var buttons = menu.dialog("option", "buttons");
//$.extend(buttons, {text: label, click: function(){ alert("Added New Poker Face"); } });
buttons[label] = function () { alert("Addded New poker Face"); };
menu.dialog("option", "buttons", buttons);
I had even used extend to overwrite the buttons list, which is commented above no luck
plz any work around for this
The doc says the return value of .dialog("option", "buttons") can either be an object {label1: click1, label2: click2, ...} or an array [{"text": label1, "click": click1}, {"text": label2, "click": click2}, ...].
Have you checked the format of buttons ? If it is an array, you should .push() your new button.
We can do something like below that worked for me.....
//gets the list of buttons.
var buttons = menu.dialog("option", "buttons");
//Adds the new button to the existing list of buttons.
buttons.push({ text: label, click: function () { alert("Addded New poker Face"); } });
//Gives the new list of buttons to the dialog to display.
menu.dialog("option", "buttons", buttons);
i've got the following code taken and slightly modified by the original example page:
http://jsfiddle.net/zK3Wc/21/
somehow, if you use the arrow down button to go through the list, it shows the values (digits) in the search field, instead of the label, but the code says, that on the select event, the label should be set as the value.
what i would need is to display the label in the searchfield instead of the digits, but if i click on an item, it has the digit value in the url, the same when using the arrow down button.
Ramo
Add a focus event:
focus: function( event, ui ) {
$( ".project" ).val( ui.item.label );
return false;
},
http://jsfiddle.net/zK3Wc/26/
For me, I forgot to put return false; in the select: callback function