SmartGWT TextItem - focusInItem() method not working? - window

I have a search form that opens in a com.smartgwt.client.widgets.Window.Window(). In it, I have a VLayout, in which I have a search form:
DynamicForm search = new DynamicForm();
// setMargin, setTitle, setNumCols
TextItem name = new TextItem();
name.setFormatOnFocusChange(true);
//setEditorValueFormatter, etc.
search.setFields(/*some fields*/, name, /*other fields*/);
name.focusInItem();
And the focus is not in the item (it's nowhere). Why is that so?
Thank you in advance!
EDIT:
Here is the code of the two Mediators:
public class MainMediator extends Mediator {
private Window popup = new Window();
protected void initView(){
// here I have a Form with fields and icon on one TextItem, on which I do:
searchField.addIconClickHandler(new IconClickHandler() {
popup = new Window();
popup.setIsModal(true);
popup.setShowModalMask(true);
});
}
public final void handleNotification(final INotification notification){
// if the right notification is sent, execute this code:
PopupMediator m = (PopupMediator) this.getFacade().retreiveMediator(PopupMediator.NAME);
VLayout popupLayout = (VLayout) m.getViewComponent();
popup.addItem(popupLayout);
popup.show();
}
}
public class PopupMediator extends Mediator {
protected void initView(){
viewComponent = new VLayout();
DynamicForm searchForm = new DynamicForm();
// searchForm props
TextItem name = new TextItem();
// name props and some other fields
searchForm.setFields(name /* and the others */);
VLayout searchFormContainer = new VLayout();
// searchFormContainer props
searchFormContainer.setMembers(seachForm);
name.focusInItem(); // not working on popup shown
HLayout searchContainer = new HLayout();
// searchContainer props
searchContainer.setMembers(grid1, searchFormContainer);
VLayout container = new VLayout();
// container props
container.setMembers (searchContainer, grid2);
((VLayout)viewComponent).setMembers(container, buttons);
}

You're getting this problem because formitem.focusInItem() works only after the formitem is drawn or say rendered in the browser. Adding the formitem in DynamicForm does not draw it.
I don't know where you're placing the DynamicForm, but to understand it completely, look at the following code:
Window window = new Window();
window.setSize("900px", "500px");
VLayout layout = new VLayout();
DynamicForm dynamicForm = new DynamicForm();
dynamicForm.setSize("800px", "400px");
TextItem item = new TextItem();
dynamicForm.setFields(item);
item.focusInItem(); // This won't work.
layout.addMember(dynamicForm);
window.addItem(layout);
item.focusInItem(); // This won't work.
window.show();
item.focusInItem(); // This will work.
So change your code accordingly.

Not sure how you receive handleNotification() callbacks, but you shouldn't use window.addItem() in it.
That will cause multiple items to be added/overwritten each time callback is called.
If handleNotification() callback is required, it should be only used for window.show(), plus any form field population/setting focus/etc.
If the content of Window is NOT going to change from one callback to another, initialize window layout during window creation.
If content of Window is GOING to change from one callback to another, you will need to remove previously added items.
Here's a simple working implementation that popup the window on a button click and set focus on a given field.
TextItem name1 = new TextItem("name1", "Name 1");
final TextItem name2 = new TextItem("name2", "Name 2"); // setting focus to name2
TextItem name3 = new TextItem("name3", "Name 3");
final DynamicForm searchForm = new DynamicForm();
// searchForm.setAutoFocus(true); // sets focus to first focusable field
searchForm.setFields(name1, name2, name3);
VLayout searchFormContainer = new VLayout();
searchFormContainer.setMembers(searchForm);
final Window window = new Window();
window.setIsModal(true);
window.setShowModalMask(true);
window.setAutoCenter(true);
window.setSize("400px", "300px");
window.addItem(searchFormContainer);
Button button = new Button("Search");
button.addClickHandler(new ClickHandler() {
public void onClick(ClickEvent event) {
window.show();
name2.focusInItem();
// searchForm.focusInItem(name2); // this also works
}
});
Its possible to use DynamicForm.setAutoFocus to automatically focus on first focusable field in the form.

Why don't you try to focus on the form itself:
search.focus();

Related

FontAwesome icon with normal text in Xamarin

I want to make a button that has a small icon (from FontAwesome) and text on it in my Xamarin app. I know I can just make a button but the problem is that I will require two fonts (the standard font for text and FontAwesome for the icon). Would anyone happen to know how I can do this, or if there is another way to achieve what I want?
Thanks!
As the json mentioned, I just made a simple implementation.
Create a new class inherit from Label, set FormattedText to combine the string(standard and icon), and add tap gesture on it .
public class MyLabel : Label
{
public delegate void MyHandler(object sender, EventArgs e);
public event MyHandler myEvent;
public MyLabel(string _myIcon, string _myText)
{
//build the UI
FormattedString text = new FormattedString();
text.Spans.Add(new Span { Text = _myIcon ,FontFamily= "FontAwesome5Free-Regular" });
text.Spans.Add(new Span { Text = _myText, TextColor = Color.Red ,BackgroundColor = Color.Blue });
FormattedText = text;
//tap event
TapGestureRecognizer tap = new TapGestureRecognizer();
tap.Tapped += (sender,e) => {
myEvent(sender,e);
};
}
}
Usage
MyLabel label = new MyLabel("", "test");
label.myEvent += (sener,e) =>
{
//do something when tapping
};
Content = label;
For how to integrate FontAwesome in Xamarin.Forms ,refer to
https://montemagno.com/xamarin-forms-custom-fonts-everywhere/.

Xamarin Forms : Add ItemSource on focus

Am trying to load ItemSource of a picker when the picker is focused.
But the data is not loaded on 1st focus.
here is the code sample
List<object> itmSrc;
Picker picker = new Picker();
itmSrc = Controls[i].ItemSource;
picker.Focused += BindItemSourceOnFocus;
public void BindItemSourceOnFocus(object sender, FocusEventArgs e)
{
var p = e.VisualElement as Picker;
p.ItemsSource = itmSrc;
}
If any other different approach is possible, let me know.
You can do it adding items on an async method, or another thread. Load the data on view focus is just transferring the issue to another place, and it gives a bad user experience at all.
If you run a code block inside a Task.Run(), for example, this code will be executed on another thread, and the interface should not hang on data loading.
Something like this:
public class MyPage : ContentPage
{
List<object> itmSrc;
Picker picker;
public MyPage()
{
// Your stuff goes here
itmSrc = new List<object>();
picker = new Picker();
StackLayout content = new StackLayout();
content.Crindren.Add(picker);
this.Content = content;
Task.Run(() => LoadData());
}
private void LoadData()
{
// Get your data from anywhere and put it on the itemSrc from here.
// Then...
picker.ItemsSource = itmSrc;
}
}
I hope it helps.

RecyclerView Click event

I have created a RecyclerView adapter and I'm trying to start an activity when a row is clicked:
public override OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
MyViewHolder viewHolder = (MyViewHolder)holder;
viewHolder.MyView.Click += (sender, e) =>
{
var context = viewHolder.MyView.Context;
var intent = new Intent(context, typeof(DetailActivity));
context.StartActivity(intent);
}
}
When I click the first row it will take me to the activity like I want. If I scroll down so that the first row is rebound and then scroll back to the top again and then click the first row then my Click event fires twice. Once for the first row that was bound and then again for a row that was bound when I scrolled.
Is there an event you need to handle to unregister the click events?
I believe the standard pattern is to setup your clickhandlers in the constructor of the ViewHolder. Then in OnBindViewHolder, you update the Views/Data inside the ViewHolder.
Something like this (not compiled code):
Adapter:
public override OnBindViewHolder()
{
MyViewHolder viewHolder = (MyViewHolder)holder;
viewHolder.SetData(whatever data you care about);
}
MyViewHolder:
public MyViewHolder(View view) : base(view)
{
MainView = view;
MainView.Click += (sender, e) =>
{
var context = MainView.Context;
var intent = new Intent(context, typeof(DetailActivity));
context.StartActivity(intent);
}
}
Doing it this way keeps the Adapter cleaner by putting business logic in the ViewHolder, and also prevents your click handlers from being constantly setup and torn down as you scroll.

Listview from arrayadapter filled text file always one update behind

I have a listview that I want to update with information from a textfile (rollcall.txt). Each time rollcall.txt is updated I am calling rollcall() (code below). The data is updated correctly in the text file before rollcall() is called, I have checked. The problem I have is that the listview doesnt show the updated entry until the next time I call rollcall() (I.E it always appears to be one update step behind).
Where am I going wrong?
public void rollcall(){
String[] splitdata = null;
try{
File myFile = new File(Environment.getExternalStorageDirectory() + "/rollcall.txt");
FileInputStream fIn = new FileInputStream(myFile);
BufferedReader myReader = new BufferedReader(
new InputStreamReader(fIn));
String aDataRow = "";
String aBuffer = "";
while ((aDataRow = myReader.readLine()) != null) {
aBuffer += aDataRow + "\n";
}
splitdata = aBuffer.split("`"); //recover the file and split it based on `
myReader.close();
}
catch (Exception e) {
Toast.makeText(getBaseContext(), e.getMessage(),
Toast.LENGTH_SHORT).show();
}
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this, R.layout.logbooklayout, splitdata);
lv1.setAdapter(adapter);
adapter.notifyDataSetChanged(); //called to ensure updated data is refreshed into listview without reload
EDIT: rollcall is called from this method:
public void onClick(View v) {
if (v==badd){
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("ROLLCALL"); //Set Alert dialog title here
alert.setMessage("Enter data: "); //Message here
// Set an EditText view to get user input
final EditText input = new EditText(this);
alert.setView(input);
alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
//You will get as string input data in this variable.
// here we convert the input to a string and show in a toast.
add = input.getEditableText().toString();
try {
File myFile = new File(Environment.getExternalStorageDirectory() + "/rollcall.txt");
myFile.createNewFile();
FileOutputStream fOut = new FileOutputStream(myFile, true);
OutputStreamWriter myOutWriter = new OutputStreamWriter(fOut);
myOutWriter.append(add);
myOutWriter.append("`"); // ` used to split the file down later in lv section
myOutWriter.close();
fOut.close();
}
catch (Exception e) {
Toast.makeText(getBaseContext(), e.getMessage(),
Toast.LENGTH_SHORT).show();
}
} // End of onClick(DialogInterface dialog, int whichButton)
}); //End of alert.setPositiveButton
alert.setNegativeButton("CANCEL", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Canceled.
dialog.cancel();
}
}); //End of alert.setNegativeButton
AlertDialog alertDialog = alert.create();
alertDialog.show();
rollcall();
}//end badd
}
Thanks for the help, I am new to using arrayadapters.
Andy
Short answer to your question is everything in UI thread is asynchronous and unless you somehow manage to freeze/lock the whole application you can't make the rest of your UI wait for your alert to grab the input. So long before you press "OK" button in your alert, your rollcall() method is being called from your onClick() function and whatever is inside your .txt file is being read/displayed on your UI, right behind your alert dialog hanging on for you to press one of the buttons, asynchronously.
Maybe the fastest solution to what you want to achieve is to call your rollcall() function somewhere else, after you confirm that your adapter's feeding data has actually been changed. If you must call it from within onClick() function, without questioning your reasons to do so, you should call it inside the try{} block, right after you close the output stream.
Like this:
try {
File myFile = new File(Environment
.getExternalStorageDirectory() + "/rollcall.txt");
myFile.createNewFile();
FileOutputStream fOut = new FileOutputStream(myFile, true);
OutputStreamWriter myOutWriter = new OutputStreamWriter(
fOut);
myOutWriter.append(add);
myOutWriter.append("`"); // ` used to split the
// file down later
// in lv section
myOutWriter.close();
fOut.close();
rollcall();
}
The reason this "works" is you already declared the listener for your "OK" button and whenever you press it, whatever inside your EditText input will be written on file. In order to make it work as before I think you need superhuman skills to write some text on alert dialog and click on button before rollcall() function is called in the same scope.
Obviously the better way to do update the list view is to be able to use adapter.notifyDataSetChanged() but I believe you should call it somewhere else than where you write on your file and in that case your adapter must be declared outside the scope of rollcall() function.
Anyways in order to show how it all goes on I created a simple(rather ugly) android application and put some logs on where the mysterious stuff is happening:
public class MainActivity extends Activity {
private ListView lv1;
private Button refreshButton;
ArrayAdapter<String> adapter;
String[] splitdata;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
refreshButton = (Button) findViewById(R.id.refreshButton);
lv1 = (ListView) findViewById(R.id.someTextViewId);
refreshButton.setOnClickListener(myButtonhandler);
splitdata = null;
}
View.OnClickListener myButtonhandler = new View.OnClickListener() {
public void onClick(View v) {
Log.d("main", "la noliy");
someFunction();
}
};
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
public void someFunction() {
AlertDialog.Builder alert = new AlertDialog.Builder(this);
alert.setTitle("ROLLCALL"); // Set Alert dialog title here
alert.setMessage("Enter data: "); // Message here
// Set an EditText view to get user input
final EditText input = new EditText(this);
alert.setView(input);
alert.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// You will get as string input data in this
// variable.
// here we convert the input to a string and show in
// a toast.
String add = input.getEditableText().toString();
try {
File myFile = new File(Environment
.getExternalStorageDirectory() + "/rollcall.txt");
myFile.createNewFile();
FileOutputStream fOut = new FileOutputStream(myFile, true);
OutputStreamWriter myOutWriter = new OutputStreamWriter(
fOut);
myOutWriter.append(add);
myOutWriter.append("`"); // ` used to split the
// file down later
// in lv section
myOutWriter.close();
fOut.close();
if (splitdata.length > 0) {
rollcall(new String("call from inside"));
}
} catch (Exception e) {
Toast.makeText(getBaseContext(), e.getMessage(),
Toast.LENGTH_SHORT).show();
}
} // End of onClick(DialogInterface dialog, int
// whichButton)
}); // End of alert.setPositiveButton
alert.setNegativeButton("CANCEL",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int whichButton) {
// Canceled.
dialog.cancel();
}
}); // End of alert.setNegativeButton
AlertDialog alertDialog = alert.create();
alertDialog.show();
Log.d("someFunction", "before rollcall");
Log.d("someFunction", "location: "
+ Environment.getExternalStorageDirectory().getAbsolutePath());
rollcall(new String("call from outside"));
Log.d("someFunction", "after rollcall");
}// end badd
public void rollcall(String message) {
Log.d("rollcall", message);
try {
File myFile = new File(Environment.getExternalStorageDirectory()
+ "/rollcall.txt");
FileInputStream fIn = new FileInputStream(myFile);
BufferedReader myReader = new BufferedReader(new InputStreamReader(
fIn));
String aDataRow = "";
String aBuffer = "";
while ((aDataRow = myReader.readLine()) != null) {
aBuffer += aDataRow + "\n";
}
splitdata = aBuffer.split("`"); // recover the file and split it
// based on `
myReader.close();
} catch (Exception e) {
Toast.makeText(getBaseContext(), e.getMessage(), Toast.LENGTH_SHORT)
.show();
}
int length = splitdata.length;
for (int i = 0; i < length; i++) {
Log.d("rollcall", splitdata[i]);
}
adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, splitdata);
lv1.setAdapter(adapter);
}
}
I put a button and an onClickListener to it. The first time you press the button everything is called, listview is updated and your dialog is hanging on your screen for either of the buttons to be pressed:
And you will see a log like this:
07-26 04:09:20.802: D/someFunction(11273): before rollcall
07-26 04:09:20.802: D/someFunction(11273): location: /mnt/sdcard
07-26 04:09:20.802: D/rollcall(11273): call from outside
07-26 04:09:20.802: D/rollcall(11273): some data
07-26 04:09:20.802: D/rollcall(11273): some other data
07-26 04:09:20.812: D/someFunction(11273): after rollcall
You can see that rollcall() has been called from outside and not inside of your try/catch block since there is also another call from there to rollcall(). But when you press the button your try/catch block will do it's job inside your onClick() function and rollcall() will be called afterwards. Hence your listview wil be updated with new data you just entered in the dialog:
Here is the final part of log right after you press "OK" you can see that rollcall() is being called and it can read the new data:
07-26 04:09:46.347: D/rollcall(11273): call from inside
07-26 04:09:46.357: D/rollcall(11273): some data
07-26 04:09:46.357: D/rollcall(11273): some other data
07-26 04:09:46.357: D/rollcall(11273): new data
Finally, I'm sure there are a lot of ugliness in this whole approach to your problem. Bottom line is you need to know that everything happening in the UI thread is asynchronous and no one is waiting for you to enter data inside your dialog in that onClick() function. You should update your listview somewhere else with a more elegant approach in case your application throws an exception for example around that try/catch block. At least maybe you should add a finally{} block at the end of it and update your listview in there even though the try part fails. Hope this answered your question:)
PS. For those who want to try this at home, remember to provide a TextView id from your layout.xml file to the findViewById() function to get the ListView reference in your code, not an actual ListView id. Yeah, I know...
call adapter.notifyDataSetChanged() everytime you update your adapter, then listview will automatically be updated
I suggest you run rollcall as an asychronous task for 2 reasons. First, it will not stop your UI when rollcall() is running.
Second, you will be able to call onPostExecute(Object o) wher you can call `adapter.notifyDataSetChanged(); '

Why would a QDialog not show when called?

I am new to the whole QT things, so please bear with me :-)
I am working on a program that has a QMainWindow with a couple of menu options. I then use QActions to call functions to do something with the menu options.
I have put together a QDialog, which shows when called directly, but not when called through the QAction dialog.
Code:
Constructor Code
View::View(QWidget *parent):QMainWindow(parent)
{
QWidget *topFiller = new QWidget;
topFiller->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
QVBoxLayout *layout = new QVBoxLayout;
layout->setMargin(5);
layout->addWidget(topFiller);
setLayout(layout);
createActions();
createMenus();
change(); //Works but displayed as a separate window
QWidget::setWindowTitle( "Stock Editor");
setMinimumSize(300, 300);
resize(680, 520);
}
QAction Snippet
changeact = new QAction(tr("&Change"), this);
connect(saveact, SIGNAL(triggered()), this, SLOT(change()));
Change() Function
void View::change()
{
QDialog *dialogWin = new QDialog(this);
dialogWin->setWindowTitle("Add Item");
QFormLayout *formLayout = new QFormLayout();
QLineEdit *barcodeLineEdit = new QLineEdit;
QLabel *barcodeLabel = new QLabel("Barcode");
QLineEdit *descLineEdit = new QLineEdit;
QLabel *descLabel = new QLabel("Description");
QSpinBox *stockSpinBox = new QSpinBox;
QLabel *stockLabel = new QLabel("Stock");
QSpinBox *priceSpinBox = new QSpinBox;
QLabel *priceLabel = new QLabel("Price");
QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok
| QDialogButtonBox::Cancel);
// connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
// connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
formLayout->addRow(barcodeLabel, barcodeLineEdit);
formLayout->addRow(descLabel, descLineEdit);
formLayout->addRow(stockLabel, stockSpinBox);
formLayout->addRow(priceLabel, priceSpinBox);
QVBoxLayout *mainLayout = new QVBoxLayout(dialogWin);
mainLayout->addLayout(formLayout);
mainLayout->addWidget(buttonBox);
dialogWin->setLayout(mainLayout);
dialogWin->activateWindow();
dialogWin->show();
}
How can I get the box to appear on top of the Main Window when called?
Thanks for the assistance

Resources