mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 695485 - Native UI for select elements. r=mfinkle
This commit is contained in:
parent
5215d252e5
commit
19d77538ea
@ -1540,7 +1540,7 @@ public class GeckoAppShell
|
||||
final JSONObject geckoObject = json.getJSONObject("gecko");
|
||||
String type = geckoObject.getString("type");
|
||||
|
||||
if (type.equals("prompt")) {
|
||||
if (type.equals("Prompt:Show")) {
|
||||
if (sPromptQueue == null)
|
||||
sPromptQueue = new SynchronousQueue<String>();
|
||||
getHandler().post(new Runnable() {
|
||||
@ -1548,6 +1548,7 @@ public class GeckoAppShell
|
||||
getPromptService().processMessage(geckoObject);
|
||||
}
|
||||
});
|
||||
|
||||
String promptServiceResult = "";
|
||||
try {
|
||||
while (null == (promptServiceResult = sPromptQueue.poll(1, TimeUnit.MILLISECONDS))) {
|
||||
|
@ -147,6 +147,8 @@ RES_LAYOUT = \
|
||||
res/layout/notification_progress_text.xml \
|
||||
res/layout/tabs_row.xml \
|
||||
res/layout/tabs_tray.xml \
|
||||
res/layout/list_item_header.xml \
|
||||
res/layout/select_dialog_list.xml \
|
||||
$(NULL)
|
||||
|
||||
RES_VALUES = \
|
||||
|
@ -44,19 +44,26 @@ import android.util.Log;
|
||||
import java.lang.String;
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.ViewGroup.LayoutParams;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.OnItemClickListener;
|
||||
import android.widget.TextView;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.CheckedTextView;
|
||||
import android.widget.EditText;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.DialogInterface.OnClickListener;
|
||||
import android.content.DialogInterface.OnCancelListener;
|
||||
import android.content.DialogInterface.OnMultiChoiceClickListener;
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONObject;
|
||||
import android.text.method.PasswordTransformationMethod;
|
||||
@ -64,10 +71,10 @@ import android.graphics.Color;
|
||||
import android.text.InputType;
|
||||
import android.app.AlertDialog;
|
||||
|
||||
public class PromptService implements OnClickListener, OnCancelListener {
|
||||
public class PromptService implements OnClickListener, OnCancelListener, OnItemClickListener {
|
||||
private PromptInput[] mInputs;
|
||||
private AlertDialog mDialog = null;
|
||||
private String[] mMenuItems = null;
|
||||
private static final String LOG_NAME = "GeckoPromptService";
|
||||
|
||||
private class PromptButton {
|
||||
public String label = "";
|
||||
@ -158,22 +165,51 @@ public class PromptService implements OnClickListener, OnCancelListener {
|
||||
}
|
||||
}
|
||||
|
||||
public void Show(String aTitle, String aText, PromptButton[] aButtons) {
|
||||
public void Show(String aTitle, String aText, PromptButton[] aButtons, PromptListItem[] aMenuList, boolean aMultipleSelection) {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(GeckoApp.mAppContext);
|
||||
if (!aTitle.equals("")) {
|
||||
builder.setTitle(aTitle);
|
||||
}
|
||||
|
||||
if (mMenuItems.length > 0) {
|
||||
builder.setItems(mMenuItems, this);
|
||||
} else {
|
||||
if (!aText.equals("")) {
|
||||
builder.setMessage(aText);
|
||||
}
|
||||
if (!aText.equals("")) {
|
||||
builder.setMessage(aText);
|
||||
}
|
||||
|
||||
int length = mInputs.length;
|
||||
if (length == 1) {
|
||||
if (aMenuList.length > 0) {
|
||||
int resourceId = android.R.layout.select_dialog_item;
|
||||
if (mSelected.length > 0) {
|
||||
if (aMultipleSelection) {
|
||||
resourceId = android.R.layout.select_dialog_multichoice;
|
||||
} else {
|
||||
resourceId = android.R.layout.select_dialog_singlechoice;
|
||||
}
|
||||
}
|
||||
PromptListAdapter adapter = new PromptListAdapter(GeckoApp.mAppContext, resourceId, aMenuList);
|
||||
if (mSelected.length > 0) {
|
||||
if (aMultipleSelection) {
|
||||
LayoutInflater inflater = GeckoApp.mAppContext.getLayoutInflater();
|
||||
adapter.listView = (ListView) inflater.inflate(R.layout.select_dialog_list, null);
|
||||
adapter.listView.setOnItemClickListener(this);
|
||||
builder.setInverseBackgroundForced(true);
|
||||
adapter.listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
adapter.listView.setAdapter(adapter);
|
||||
builder.setView(adapter.listView);
|
||||
} else {
|
||||
int selectedIndex = -1;
|
||||
for (int i = 0; i < mSelected.length; i++) {
|
||||
if (mSelected[i]) {
|
||||
selectedIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
mSelected = null;
|
||||
builder.setSingleChoiceItems(adapter, selectedIndex, this);
|
||||
}
|
||||
} else {
|
||||
builder.setAdapter(adapter, this);
|
||||
}
|
||||
} else if (length == 1) {
|
||||
builder.setView(mInputs[0].getView());
|
||||
} else if (length > 1) {
|
||||
LinearLayout linearLayout = new LinearLayout(GeckoApp.mAppContext);
|
||||
@ -215,43 +251,55 @@ public class PromptService implements OnClickListener, OnCancelListener {
|
||||
JSONObject ret = new JSONObject();
|
||||
try {
|
||||
int button = -1;
|
||||
if (mMenuItems.length > 0) {
|
||||
ListView list = mDialog.getListView();
|
||||
if (list != null || mSelected != null) {
|
||||
button = aWhich;
|
||||
if (mSelected != null) {
|
||||
JSONArray selected = new JSONArray();
|
||||
for (int i = 0; i < mSelected.length; i++) {
|
||||
selected.put(mSelected[i]);
|
||||
}
|
||||
ret.put("button", selected);
|
||||
} else {
|
||||
ret.put("button", button);
|
||||
}
|
||||
} else {
|
||||
switch(aWhich) {
|
||||
case DialogInterface.BUTTON_POSITIVE : button = 0; break;
|
||||
case DialogInterface.BUTTON_NEUTRAL : button = 1; break;
|
||||
case DialogInterface.BUTTON_NEGATIVE : button = 2; break;
|
||||
}
|
||||
ret.put("button", button);
|
||||
}
|
||||
ret.put("button", button);
|
||||
if (mInputs != null) {
|
||||
for (int i = 0; i < mInputs.length; i++) {
|
||||
ret.put(mInputs[i].getName(), mInputs[i].getValue());
|
||||
}
|
||||
}
|
||||
} catch(Exception ex) {
|
||||
Log.i("GeckoShell", "Error building return: " + ex);
|
||||
Log.i(LOG_NAME, "Error building return: " + ex);
|
||||
}
|
||||
|
||||
if (mDialog != null) {
|
||||
mDialog.dismiss();
|
||||
}
|
||||
|
||||
finishDialog(ret.toString());
|
||||
}
|
||||
|
||||
private boolean[] mSelected = null;
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
mSelected[position] = !mSelected[position];
|
||||
}
|
||||
|
||||
public void onCancel(DialogInterface aDialog) {
|
||||
JSONObject ret = new JSONObject();
|
||||
try {
|
||||
int button = -1;
|
||||
ret.put("button", button);
|
||||
} catch(Exception ex) {
|
||||
Log.i("GeckoShell", "Error building return: " + ex);
|
||||
}
|
||||
finishDialog(ret.toString());
|
||||
onClick(aDialog, -1);
|
||||
}
|
||||
|
||||
public void finishDialog(String aReturn) {
|
||||
mInputs = null;
|
||||
mDialog = null;
|
||||
mMenuItems = null;
|
||||
Log.i("GeckoShell", "finish " + aReturn);
|
||||
mSelected = null;
|
||||
try {
|
||||
GeckoAppShell.sPromptQueue.put(aReturn);
|
||||
} catch(Exception ex) { }
|
||||
@ -291,16 +339,20 @@ public class PromptService implements OnClickListener, OnCancelListener {
|
||||
} catch(Exception ex) { }
|
||||
}
|
||||
|
||||
mMenuItems = getStringArray(geckoObject, "listitems");
|
||||
this.Show(title, text, promptbuttons);
|
||||
PromptListItem[] menuitems = getListItemArray(geckoObject, "listitems");
|
||||
mSelected = getBooleanArray(geckoObject, "selected");
|
||||
boolean multiple = false;
|
||||
try {
|
||||
multiple = geckoObject.getBoolean("multiple");
|
||||
} catch(Exception ex) { }
|
||||
this.Show(title, text, promptbuttons, menuitems, multiple);
|
||||
}
|
||||
|
||||
private String[] getStringArray(JSONObject aObject, String aName) {
|
||||
JSONArray items = new JSONArray();
|
||||
try {
|
||||
items = aObject.getJSONArray(aName);
|
||||
} catch(Exception ex) {
|
||||
}
|
||||
} catch(Exception ex) { }
|
||||
int length = items.length();
|
||||
String[] list = new String[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
@ -310,4 +362,105 @@ public class PromptService implements OnClickListener, OnCancelListener {
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private boolean[] getBooleanArray(JSONObject aObject, String aName) {
|
||||
JSONArray items = new JSONArray();
|
||||
try {
|
||||
items = aObject.getJSONArray(aName);
|
||||
} catch(Exception ex) { }
|
||||
int length = items.length();
|
||||
boolean[] list = new boolean[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
try {
|
||||
list[i] = items.getBoolean(i);
|
||||
} catch(Exception ex) { }
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private PromptListItem[] getListItemArray(JSONObject aObject, String aName) {
|
||||
JSONArray items = new JSONArray();
|
||||
try {
|
||||
items = aObject.getJSONArray(aName);
|
||||
} catch(Exception ex) {
|
||||
}
|
||||
int length = items.length();
|
||||
PromptListItem[] list = new PromptListItem[length];
|
||||
for (int i = 0; i < length; i++) {
|
||||
try {
|
||||
list[i] = new PromptListItem(items.getJSONObject(i));
|
||||
} catch(Exception ex) { }
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
private class PromptListItem {
|
||||
public String label = "";
|
||||
public boolean isGroup = false;
|
||||
public boolean inGroup = false;
|
||||
public boolean disabled = false;
|
||||
public int id = 0;
|
||||
PromptListItem(JSONObject aObject) {
|
||||
try { label = aObject.getString("label"); } catch(Exception ex) { }
|
||||
try { isGroup = aObject.getBoolean("isGroup"); } catch(Exception ex) { }
|
||||
try { inGroup = aObject.getBoolean("inGroup"); } catch(Exception ex) { }
|
||||
try { disabled = aObject.getBoolean("disabled"); } catch(Exception ex) { }
|
||||
try { id = aObject.getInt("id"); } catch(Exception ex) { }
|
||||
}
|
||||
}
|
||||
|
||||
public class PromptListAdapter extends ArrayAdapter<PromptListItem> {
|
||||
public ListView listView = null;
|
||||
private PromptListItem[] mList;
|
||||
private int mResourceId = -1;
|
||||
PromptListAdapter(Context context, int textViewResourceId, PromptListItem[] objects) {
|
||||
super(context, textViewResourceId, objects);
|
||||
mList = objects;
|
||||
mResourceId = textViewResourceId;
|
||||
}
|
||||
|
||||
public int getCount() {
|
||||
return mList.length;
|
||||
}
|
||||
|
||||
public PromptListItem getItem(int position) {
|
||||
return mList[position];
|
||||
}
|
||||
|
||||
public long getItemId(int position) {
|
||||
return mList[position].id;
|
||||
}
|
||||
|
||||
public View getView(int position, View convertView, ViewGroup parent) {
|
||||
PromptListItem item = getItem(position);
|
||||
int resourceId = mResourceId;
|
||||
if (item.isGroup) {
|
||||
resourceId = R.layout.list_item_header;
|
||||
}
|
||||
LayoutInflater inflater = GeckoApp.mAppContext.getLayoutInflater();
|
||||
View row = inflater.inflate(resourceId, null);
|
||||
if (!item.isGroup){
|
||||
try {
|
||||
CheckedTextView ct = (CheckedTextView)row.findViewById(android.R.id.text1);
|
||||
if (ct != null){
|
||||
ct.setEnabled(!item.disabled);
|
||||
ct.setClickable(item.disabled);
|
||||
|
||||
// Apparently just using ct.setChecked(true) doesn't work, so this
|
||||
// is stolen from the android source code as a way to set the checked
|
||||
// state of these items
|
||||
if (mSelected[position] && listView != null) {
|
||||
listView.setItemChecked(position, true);
|
||||
}
|
||||
}
|
||||
} catch (Exception ex) { }
|
||||
}
|
||||
TextView t1 = (TextView) row.findViewById(android.R.id.text1);
|
||||
if (t1 != null) {
|
||||
t1.setText(item.label);
|
||||
}
|
||||
|
||||
return row;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
10
embedding/android/resources/layout/select_dialog_list.xml
Normal file
10
embedding/android/resources/layout/select_dialog_list.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- This is used for select lists for multiple selection enabled -->
|
||||
<view xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
class="android.widget.ListView"
|
||||
android:id="@+id/select_list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:cacheColorHint="@null"
|
||||
android:scrollbars="vertical"
|
||||
android:overScrollMode="ifContentScrolls" />
|
@ -891,6 +891,8 @@ var BrowserEventHandler = {
|
||||
if (this.blockClick) {
|
||||
aEvent.stopPropagation();
|
||||
aEvent.preventDefault();
|
||||
} else {
|
||||
FormAssistant.handleClick(aEvent);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -1337,6 +1339,102 @@ var ErrorPageEventHandler = {
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
var FormAssistant = {
|
||||
show: function(aList, aElement) {
|
||||
let data = JSON.parse(sendMessageToJava({ gecko: aList }));
|
||||
let selected = data.button;
|
||||
if (!(selected instanceof Array)) {
|
||||
let temp = [];
|
||||
for (let i = 0; i < aList.listitems.length; i++) {
|
||||
temp[i] = (i == selected);
|
||||
}
|
||||
selected = temp;
|
||||
}
|
||||
this.forOptions(aElement, function(aNode, aIndex) {
|
||||
aNode.selected = selected[aIndex];
|
||||
});
|
||||
},
|
||||
|
||||
handleClick: function(aEvent) {
|
||||
let target = aEvent.target;
|
||||
while (target) {
|
||||
if (this._isSelectElement(target)) {
|
||||
let list = this.getListForElement(target);
|
||||
this.show(list, target);
|
||||
target = null;
|
||||
return true;
|
||||
}
|
||||
if (target)
|
||||
target = target.parentNode;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
_isSelectElement: function(aElement) {
|
||||
return (aElement instanceof HTMLSelectElement);
|
||||
},
|
||||
|
||||
_isOptionElement: function(aElement) {
|
||||
return aElement instanceof HTMLOptionElement;
|
||||
},
|
||||
|
||||
_isOptionGroupElement: function(aElement) {
|
||||
return aElement instanceof HTMLOptGroupElement;
|
||||
},
|
||||
|
||||
getListForElement: function(aElement) {
|
||||
let result = {
|
||||
type: "Prompt:Show",
|
||||
multiple: aElement.multiple,
|
||||
selected: [],
|
||||
listitems: []
|
||||
};
|
||||
|
||||
if (aElement.multiple) {
|
||||
result.buttons = [
|
||||
{ label: Strings.browser.GetStringFromName("selectHelper.closeMultipleSelectDialog") },
|
||||
];
|
||||
}
|
||||
|
||||
this.forOptions(aElement, function(aNode, aIndex) {
|
||||
result.listitems[aIndex] = {
|
||||
label: aNode.text || aNode.label,
|
||||
isGroup: this._isOptionGroupElement(aNode),
|
||||
inGroup: this._isOptionGroupElement(aNode.parentNode),
|
||||
disabled: aNode.disabled,
|
||||
id: aIndex
|
||||
}
|
||||
result.selected[aIndex] = aNode.selected;
|
||||
});
|
||||
return result;
|
||||
},
|
||||
|
||||
forOptions: function(aElement, aFunction) {
|
||||
let optionIndex = 0;
|
||||
let children = aElement.children;
|
||||
for (let i = 0; i < children.length; i++) {
|
||||
let child = children[i];
|
||||
if (this._isOptionGroupElement(child)) {
|
||||
aFunction.call(this, child, optionIndex);
|
||||
optionIndex++;
|
||||
|
||||
let subchildren = child.children;
|
||||
for (let j = 0; j < subchildren.length; j++) {
|
||||
let subchild = subchildren[j];
|
||||
aFunction.call(this, subchild, optionIndex);
|
||||
optionIndex++;
|
||||
}
|
||||
|
||||
} else if (this._isOptionElement(child)) {
|
||||
// This is a regular choice under no group.
|
||||
aFunction.call(this, child, optionIndex);
|
||||
optionIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var XPInstallObserver = {
|
||||
observe: function xpi_observer(aSubject, aTopic, aData) {
|
||||
switch (aTopic) {
|
||||
|
@ -156,7 +156,7 @@ Prompt.prototype = {
|
||||
if (aCheckMsg)
|
||||
aInputs.push({ type: "checkbox", label: aCheckMsg, checked: aCheckState.value });
|
||||
|
||||
let msg = { type: "prompt" };
|
||||
let msg = { type: "Prompt:Show" };
|
||||
if (aTitle) msg.title = aTitle;
|
||||
if (aText) msg.text = aText;
|
||||
msg.buttons = aButtons || [
|
||||
|
@ -248,3 +248,6 @@ appMenu.more=More
|
||||
|
||||
#Text Selection
|
||||
selectionHelper.textCopied=Text copied to clipboard
|
||||
|
||||
#Select UI
|
||||
selectHelper.closeMultipleSelectDialog=Done
|
||||
|
Loading…
Reference in New Issue
Block a user