Bug 695172 - Find in page. r=mfinkle
106
mobile/android/base/FindInPageBar.java
Normal file
@ -0,0 +1,106 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.gecko;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.EditText;
|
||||
import android.widget.RelativeLayout;
|
||||
import android.widget.RelativeLayout.LayoutParams;
|
||||
|
||||
public class FindInPageBar extends RelativeLayout implements TextWatcher, View.OnClickListener {
|
||||
private static final String LOGTAG = "GeckoFindInPagePopup";
|
||||
|
||||
private final Context mContext;
|
||||
private EditText mFindText;
|
||||
private boolean mInflated = false;
|
||||
|
||||
public FindInPageBar(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
setFocusable(true);
|
||||
}
|
||||
|
||||
public void inflateContent() {
|
||||
LayoutInflater inflater = LayoutInflater.from(mContext);
|
||||
View content = inflater.inflate(R.layout.find_in_page_content, this);
|
||||
|
||||
content.findViewById(R.id.find_prev).setOnClickListener(this);
|
||||
content.findViewById(R.id.find_next).setOnClickListener(this);
|
||||
content.findViewById(R.id.find_close).setOnClickListener(this);
|
||||
|
||||
mFindText = (EditText) content.findViewById(R.id.find_text);
|
||||
mFindText.addTextChangedListener(this);
|
||||
|
||||
// EditText styling modeled after AwesomeBar styling
|
||||
|
||||
int padding[] = { mFindText.getPaddingLeft(),
|
||||
mFindText.getPaddingTop(),
|
||||
mFindText.getPaddingRight(),
|
||||
mFindText.getPaddingBottom() };
|
||||
|
||||
Resources resources = getContext().getResources();
|
||||
GeckoStateListDrawable states = new GeckoStateListDrawable();
|
||||
states.initializeFilter(GeckoApp.mBrowserToolbar.getHighlightColor());
|
||||
states.addState(new int[] { android.R.attr.state_focused }, resources.getDrawable(R.drawable.address_bar_url_pressed));
|
||||
states.addState(new int[] { android.R.attr.state_pressed }, resources.getDrawable(R.drawable.address_bar_url_pressed));
|
||||
states.addState(new int[] { }, resources.getDrawable(R.drawable.address_bar_url_default));
|
||||
mFindText.setBackgroundDrawable(states);
|
||||
|
||||
mFindText.setPadding(padding[0], padding[1], padding[2], padding[3]);
|
||||
|
||||
mInflated = true;
|
||||
}
|
||||
|
||||
public void show() {
|
||||
if (!mInflated)
|
||||
inflateContent();
|
||||
|
||||
setVisibility(VISIBLE);
|
||||
mFindText.requestFocus();
|
||||
}
|
||||
|
||||
public void hide() {
|
||||
setVisibility(GONE);
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FindInPage:Closed", null));
|
||||
}
|
||||
|
||||
// TextWatcher implementation
|
||||
|
||||
public void afterTextChanged(Editable s) {
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FindInPage:Find", s.toString()));
|
||||
}
|
||||
|
||||
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
// ignore
|
||||
}
|
||||
|
||||
// View.OnClickListener implementation
|
||||
|
||||
public void onClick(View v) {
|
||||
switch (v.getId()) {
|
||||
case R.id.find_prev:
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FindInPage:Prev", mFindText.getText().toString()));
|
||||
break;
|
||||
case R.id.find_next:
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("FindInPage:Next", mFindText.getText().toString()));
|
||||
break;
|
||||
case R.id.find_close:
|
||||
hide();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
@ -108,6 +108,7 @@ abstract public class GeckoApp
|
||||
private static GeckoLayerClient mLayerClient;
|
||||
private AboutHomeContent mAboutHomeContent;
|
||||
private static AbsoluteLayout mPluginContainer;
|
||||
private static FindInPageBar mFindInPageBar;
|
||||
|
||||
private View mFullScreenPluginView;
|
||||
|
||||
@ -407,15 +408,17 @@ abstract public class GeckoApp
|
||||
MenuItem share = aMenu.findItem(R.id.share);
|
||||
MenuItem saveAsPDF = aMenu.findItem(R.id.save_as_pdf);
|
||||
MenuItem charEncoding = aMenu.findItem(R.id.char_encoding);
|
||||
MenuItem findInPage = aMenu.findItem(R.id.find_in_page);
|
||||
|
||||
if (tab == null || tab.getURL() == null) {
|
||||
bookmark.setEnabled(false);
|
||||
forward.setEnabled(false);
|
||||
share.setEnabled(false);
|
||||
saveAsPDF.setEnabled(false);
|
||||
findInPage.setEnabled(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bookmark.setEnabled(true);
|
||||
bookmark.setCheckable(true);
|
||||
|
||||
@ -438,6 +441,10 @@ abstract public class GeckoApp
|
||||
saveAsPDF.setEnabled(!(tab.getURL().equals("about:home") ||
|
||||
tab.getContentType().equals("application/vnd.mozilla.xul+xml")));
|
||||
|
||||
// Disable find in page for about:home, since it won't work on Java content
|
||||
if (!tab.getURL().equals("about:home"))
|
||||
findInPage.setEnabled(true);
|
||||
|
||||
charEncoding.setVisible(GeckoPreferences.getCharEncodingState());
|
||||
|
||||
return true;
|
||||
@ -512,6 +519,9 @@ abstract public class GeckoApp
|
||||
case R.id.char_encoding:
|
||||
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("CharEncoding:Get", null));
|
||||
return true;
|
||||
case R.id.find_in_page:
|
||||
mFindInPageBar.show();
|
||||
return true;
|
||||
default:
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
@ -1782,6 +1792,7 @@ abstract public class GeckoApp
|
||||
}
|
||||
|
||||
mPluginContainer = (AbsoluteLayout) findViewById(R.id.plugin_container);
|
||||
mFindInPageBar = (FindInPageBar) findViewById(R.id.find_in_page);
|
||||
|
||||
mDoorHangerPopup = new DoorHangerPopup(this);
|
||||
mFormAssistPopup = (FormAssistPopup) findViewById(R.id.form_assist_popup);
|
||||
|
@ -52,6 +52,8 @@ public final class GeckoViewsFactory implements LayoutInflater.Factory {
|
||||
return new FormAssistPopup(context, attrs);
|
||||
else if (TextUtils.equals(viewName, "LinkTextView"))
|
||||
return new LinkTextView(context, attrs);
|
||||
else if (TextUtils.equals(viewName, "FindInPageBar"))
|
||||
return new FindInPageBar(context, attrs);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -57,6 +57,7 @@ FENNEC_JAVA_FILES = \
|
||||
DoorHanger.java \
|
||||
DoorHangerPopup.java \
|
||||
Favicons.java \
|
||||
FindInPageBar.java \
|
||||
FloatUtils.java \
|
||||
FormAssistPopup.java \
|
||||
GeckoActionBar.java \
|
||||
@ -254,6 +255,7 @@ RES_LAYOUT = \
|
||||
res/layout/browser_toolbar.xml \
|
||||
res/layout/doorhangerpopup.xml \
|
||||
res/layout/doorhanger.xml \
|
||||
res/layout/find_in_page_content.xml \
|
||||
res/layout/gecko_app.xml \
|
||||
res/layout/launch_app_list.xml \
|
||||
res/layout/launch_app_listitem.xml \
|
||||
@ -394,6 +396,10 @@ RES_DRAWABLE_BASE = \
|
||||
res/drawable/doorhanger_bg.9.png \
|
||||
res/drawable/doorhanger_shadow_bg.9.png \
|
||||
res/drawable/doorhanger_popup_bg.9.png \
|
||||
res/drawable/find_close.png \
|
||||
res/drawable/find_next.png \
|
||||
res/drawable/find_prev.png \
|
||||
res/drawable/larry_green.png \
|
||||
res/drawable/larry_blue.png \
|
||||
res/drawable/larry_green.png \
|
||||
res/drawable/site_security_identified.png \
|
||||
@ -457,6 +463,9 @@ RES_DRAWABLE_HDPI = \
|
||||
res/drawable-hdpi/doorhanger_bg.9.png \
|
||||
res/drawable-hdpi/doorhanger_shadow_bg.9.png \
|
||||
res/drawable-hdpi/doorhanger_popup_bg.9.png \
|
||||
res/drawable-hdpi/find_close.png \
|
||||
res/drawable-hdpi/find_next.png \
|
||||
res/drawable-hdpi/find_prev.png \
|
||||
res/drawable-hdpi/larry_blue.png \
|
||||
res/drawable-hdpi/larry_green.png \
|
||||
res/drawable-hdpi/site_security_identified.png \
|
||||
@ -543,6 +552,9 @@ RES_DRAWABLE_XHDPI_V11 = \
|
||||
res/drawable-xhdpi-v11/doorhanger_bg.9.png \
|
||||
res/drawable-xhdpi-v11/doorhanger_shadow_bg.9.png \
|
||||
res/drawable-xhdpi-v11/doorhanger_popup_bg.9.png \
|
||||
res/drawable-xhdpi-v11/find_close.png \
|
||||
res/drawable-xhdpi-v11/find_next.png \
|
||||
res/drawable-xhdpi-v11/find_prev.png \
|
||||
res/drawable-xhdpi-v11/urlbar_stop.png \
|
||||
res/drawable-xhdpi-v11/larry_blue.png \
|
||||
res/drawable-xhdpi-v11/larry_green.png \
|
||||
|
@ -94,6 +94,14 @@
|
||||
<!ENTITY share "Share">
|
||||
<!ENTITY share_title "Share via">
|
||||
<!ENTITY save_as_pdf "Save as PDF">
|
||||
<!ENTITY find_in_page "Find in Page">
|
||||
|
||||
<!-- Localization note (find_text, find_prev, find_next, find_close) : These strings are used
|
||||
as alternate text for accessibility. They are not visible in the UI. -->
|
||||
<!ENTITY find_text "Find in Page">
|
||||
<!ENTITY find_prev "Previous">
|
||||
<!ENTITY find_next "Next">
|
||||
<!ENTITY find_close "Close">
|
||||
|
||||
<!ENTITY contextmenu_open_new_tab "Open in New Tab">
|
||||
<!ENTITY contextmenu_remove_history "Remove">
|
||||
|
BIN
mobile/android/base/resources/drawable-hdpi/find_close.png
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
mobile/android/base/resources/drawable-hdpi/find_next.png
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
mobile/android/base/resources/drawable-hdpi/find_prev.png
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
mobile/android/base/resources/drawable-xhdpi-v11/find_close.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
mobile/android/base/resources/drawable-xhdpi-v11/find_next.png
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
mobile/android/base/resources/drawable-xhdpi-v11/find_prev.png
Normal file
After Width: | Height: | Size: 1.8 KiB |
BIN
mobile/android/base/resources/drawable/find_close.png
Normal file
After Width: | Height: | Size: 1.7 KiB |
BIN
mobile/android/base/resources/drawable/find_next.png
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
mobile/android/base/resources/drawable/find_prev.png
Normal file
After Width: | Height: | Size: 1.2 KiB |
@ -27,6 +27,10 @@
|
||||
android:layout_height="fill_parent"
|
||||
android:background="@drawable/abouthome_bg_repeat"/>
|
||||
|
||||
</RelativeLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
<org.mozilla.gecko.FindInPageBar android:id="@+id/find_in_page"
|
||||
style="@style/FindBar"
|
||||
visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<merge xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<EditText android:id="@+id/find_text"
|
||||
android:contentDescription="@string/find_text"
|
||||
android:singleLine="true"
|
||||
android:textColor="#000000"
|
||||
android:inputType="text"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="5dip"
|
||||
android:layout_marginRight="5dip"
|
||||
android:paddingLeft="15dip"
|
||||
android:paddingRight="15dip"
|
||||
android:imeOptions="actionSearch"
|
||||
android:selectAllOnFocus="true"
|
||||
android:gravity="center_vertical|left"
|
||||
android:layout_toLeftOf="@+id/find_prev"/>
|
||||
|
||||
<ImageButton android:id="@+id/find_prev"
|
||||
style="@style/FindBar.ImageButton"
|
||||
android:contentDescription="@string/find_prev"
|
||||
android:src="@drawable/find_prev"
|
||||
android:layout_toLeftOf="@+id/find_next"/>
|
||||
|
||||
<ImageButton android:id="@+id/find_next"
|
||||
style="@style/FindBar.ImageButton"
|
||||
android:contentDescription="@string/find_next"
|
||||
android:src="@drawable/find_next"
|
||||
android:layout_toLeftOf="@+id/find_close"/>
|
||||
|
||||
<ImageButton android:id="@+id/find_close"
|
||||
style="@style/FindBar.ImageButton"
|
||||
android:contentDescription="@string/find_close"
|
||||
android:src="@drawable/find_close"
|
||||
android:layout_alignParentRight="true"/>
|
||||
|
||||
</merge>
|
@ -29,6 +29,10 @@
|
||||
android:layout_height="fill_parent"
|
||||
android:background="@drawable/abouthome_bg_repeat"/>
|
||||
|
||||
</RelativeLayout>
|
||||
</RelativeLayout>
|
||||
|
||||
<org.mozilla.gecko.FindInPageBar android:id="@+id/find_in_page"
|
||||
style="@style/FindBar"
|
||||
visibility="gone"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
@ -25,6 +25,9 @@
|
||||
android:icon="@drawable/ic_menu_save_as_pdf"
|
||||
android:title="@string/save_as_pdf" />
|
||||
|
||||
<item android:id="@+id/find_in_page"
|
||||
android:title="@string/find_in_page" />
|
||||
|
||||
<item android:id="@+id/site_settings"
|
||||
android:title="@string/site_settings_title" />
|
||||
|
||||
|
@ -97,4 +97,27 @@
|
||||
<item name="android:background">@drawable/tabs_tray_bg_repeat</item>
|
||||
</style>
|
||||
|
||||
<!-- Find bar -->
|
||||
<style name="FindBar">
|
||||
<item name="android:layout_width">fill_parent</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:orientation">horizontal</item>
|
||||
<item name="android:background">#000000</item>
|
||||
<item name="android:paddingLeft">3dip</item>
|
||||
<item name="android:paddingRight">3dip</item>
|
||||
<item name="android:paddingTop">6dip</item>
|
||||
<item name="android:paddingBottom">6dip</item>
|
||||
</style>
|
||||
|
||||
<!-- Find bar - Image Button -->
|
||||
<style name="FindBar.ImageButton">
|
||||
<item name="android:layout_width">wrap_content</item>
|
||||
<item name="android:layout_height">wrap_content</item>
|
||||
<item name="android:layout_marginLeft">5dip</item>
|
||||
<item name="android:layout_marginRight">5dip</item>
|
||||
<item name="android:scaleType">fitCenter</item>
|
||||
<item name="android:layout_centerVertical">true</item>
|
||||
<item name="android:background">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
|
@ -57,6 +57,12 @@
|
||||
<string name="share">&share;</string>
|
||||
<string name="share_title">&share_title;</string>
|
||||
<string name="save_as_pdf">&save_as_pdf;</string>
|
||||
<string name="find_in_page">&find_in_page;</string>
|
||||
|
||||
<string name="find_text">&find_text;</string>
|
||||
<string name="find_prev">&find_prev;</string>
|
||||
<string name="find_next">&find_next;</string>
|
||||
<string name="find_close">&find_close;</string>
|
||||
|
||||
<string name="settings">&settings;</string>
|
||||
<string name="settings_title">&settings_title;</string>
|
||||
|
@ -191,6 +191,7 @@ var BrowserApp = {
|
||||
|
||||
NativeWindow.init();
|
||||
Downloads.init();
|
||||
FindHelper.init();
|
||||
FormAssistant.init();
|
||||
OfflineApps.init();
|
||||
IndexedDB.init();
|
||||
@ -392,6 +393,7 @@ var BrowserApp = {
|
||||
shutdown: function shutdown() {
|
||||
NativeWindow.uninit();
|
||||
FormAssistant.uninit();
|
||||
FindHelper.uninit();
|
||||
OfflineApps.uninit();
|
||||
IndexedDB.uninit();
|
||||
ViewportHandler.uninit();
|
||||
@ -3176,6 +3178,97 @@ var ErrorPageEventHandler = {
|
||||
}
|
||||
};
|
||||
|
||||
var FindHelper = {
|
||||
_find: null,
|
||||
_findInProgress: false,
|
||||
_targetTab: null,
|
||||
_initialViewport: null,
|
||||
_viewportChanged: false,
|
||||
|
||||
init: function() {
|
||||
Services.obs.addObserver(this, "FindInPage:Find", false);
|
||||
Services.obs.addObserver(this, "FindInPage:Prev", false);
|
||||
Services.obs.addObserver(this, "FindInPage:Next", false);
|
||||
Services.obs.addObserver(this, "FindInPage:Closed", false);
|
||||
},
|
||||
|
||||
uninit: function() {
|
||||
Services.obs.removeObserver(this, "FindInPage:Find", false);
|
||||
Services.obs.removeObserver(this, "FindInPage:Prev", false);
|
||||
Services.obs.removeObserver(this, "FindInPage:Next", false);
|
||||
Services.obs.removeObserver(this, "FindInPage:Closed", false);
|
||||
},
|
||||
|
||||
observe: function(aMessage, aTopic, aData) {
|
||||
switch(aTopic) {
|
||||
case "FindInPage:Find":
|
||||
this.doFind(aData);
|
||||
break;
|
||||
|
||||
case "FindInPage:Prev":
|
||||
this.findAgain(aData, true);
|
||||
break;
|
||||
|
||||
case "FindInPage:Next":
|
||||
this.findAgain(aData, false);
|
||||
break;
|
||||
|
||||
case "FindInPage:Closed":
|
||||
this.findClosed();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
doFind: function(aSearchString) {
|
||||
if (!this._findInProgress) {
|
||||
this._findInProgress = true;
|
||||
this._targetTab = BrowserApp.selectedTab;
|
||||
this._find = Cc["@mozilla.org/typeaheadfind;1"].createInstance(Ci.nsITypeAheadFind);
|
||||
this._find.init(this._targetTab.browser.docShell);
|
||||
this._initialViewport = JSON.stringify(this._targetTab.viewport);
|
||||
this._viewportChanged = false;
|
||||
}
|
||||
|
||||
let result = this._find.find(aSearchString, false);
|
||||
this.handleResult(result);
|
||||
},
|
||||
|
||||
findAgain: function(aString, aFindBackwards) {
|
||||
let result = this._find.findAgain(aFindBackwards, false);
|
||||
this.handleResult(result);
|
||||
},
|
||||
|
||||
findClosed: function() {
|
||||
if (!this._findInProgress) {
|
||||
// this should never happen
|
||||
Cu.reportError("Warning: findClosed() called while _findInProgress is false!");
|
||||
// fall through and clean up anyway
|
||||
}
|
||||
|
||||
this._find = null;
|
||||
this._findInProgress = false;
|
||||
this._targetTab = null;
|
||||
this._initialViewport = null;
|
||||
this._viewportChanged = false;
|
||||
},
|
||||
|
||||
handleResult: function(aResult) {
|
||||
if (aResult == Ci.nsITypeAheadFind.FIND_NOTFOUND) {
|
||||
if (this._viewportChanged) {
|
||||
if (this._targetTab != BrowserApp.selectedTab) {
|
||||
// this should never happen
|
||||
Cu.reportError("Warning: selected tab changed during find!");
|
||||
// fall through and restore viewport on the initial tab anyway
|
||||
}
|
||||
this._targetTab.viewport = JSON.parse(this._initialViewport);
|
||||
this._targetTab.sendViewportUpdate();
|
||||
}
|
||||
} else {
|
||||
this._viewportChanged = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var FormAssistant = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIFormSubmitObserver]),
|
||||
|
||||
|