mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 817716 - (Part 1) Add 3-dot menu to tabs panel, including a "close all tabs" option. r=bnicholson
This commit is contained in:
parent
c2214fcc07
commit
df30eb4d18
@ -256,6 +256,7 @@ size. -->
|
||||
<!ENTITY new_tab "New Tab">
|
||||
<!ENTITY new_private_tab "New Private Tab">
|
||||
<!ENTITY close_all_tabs "Close All Tabs">
|
||||
<!ENTITY close_private_tabs "Close Private Tabs">
|
||||
<!ENTITY tabs_normal "Tabs">
|
||||
<!ENTITY tabs_private "Private">
|
||||
<!ENTITY tabs_synced "Synced">
|
||||
|
@ -25,4 +25,13 @@
|
||||
android:contentDescription="@string/new_tab"
|
||||
android:background="@drawable/action_bar_button_inverse"/>
|
||||
|
||||
<ImageButton android:id="@+id/menu"
|
||||
style="@style/UrlBar.ImageButton"
|
||||
android:layout_width="@dimen/browser_toolbar_height"
|
||||
android:layout_height="@dimen/browser_toolbar_height"
|
||||
android:padding="@dimen/browser_toolbar_button_padding"
|
||||
android:src="@drawable/menu"
|
||||
android:contentDescription="@string/menu"
|
||||
android:background="@drawable/action_bar_button"/>
|
||||
|
||||
</merge>
|
||||
|
20
mobile/android/base/resources/menu-v11/tabs_menu.xml
Normal file
20
mobile/android/base/resources/menu-v11/tabs_menu.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:id="@+id/new_tab"
|
||||
android:title="@string/new_tab"/>
|
||||
|
||||
<item android:id="@+id/new_private_tab"
|
||||
android:title="@string/new_private_tab"/>
|
||||
|
||||
<item android:id="@+id/close_all_tabs"
|
||||
android:title="@string/close_all_tabs"/>
|
||||
|
||||
<item android:id="@+id/close_private_tabs"
|
||||
android:title="@string/close_private_tabs"/>
|
||||
|
||||
</menu>
|
20
mobile/android/base/resources/menu/tabs_menu.xml
Normal file
20
mobile/android/base/resources/menu/tabs_menu.xml
Normal file
@ -0,0 +1,20 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- 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/. -->
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item android:id="@+id/new_tab"
|
||||
android:title="@string/new_tab"/>
|
||||
|
||||
<item android:id="@+id/new_private_tab"
|
||||
android:title="@string/new_private_tab"/>
|
||||
|
||||
<item android:id="@+id/close_all_tabs"
|
||||
android:title="@string/close_all_tabs"/>
|
||||
|
||||
<item android:id="@+id/close_private_tabs"
|
||||
android:title="@string/close_private_tabs"/>
|
||||
|
||||
</menu>
|
@ -246,6 +246,7 @@
|
||||
<string name="new_tab">&new_tab;</string>
|
||||
<string name="new_private_tab">&new_private_tab;</string>
|
||||
<string name="close_all_tabs">&close_all_tabs;</string>
|
||||
<string name="close_private_tabs">&close_private_tabs;</string>
|
||||
<string name="tabs_normal">&tabs_normal;</string>
|
||||
<string name="tabs_private">&tabs_private;</string>
|
||||
<string name="tabs_synced">&tabs_synced;</string>
|
||||
|
@ -15,6 +15,7 @@ import org.mozilla.gecko.LightweightThemeDrawable;
|
||||
import org.mozilla.gecko.R;
|
||||
import org.mozilla.gecko.animation.PropertyAnimator;
|
||||
import org.mozilla.gecko.animation.ViewHelper;
|
||||
import org.mozilla.gecko.widget.GeckoPopupMenu;
|
||||
import org.mozilla.gecko.widget.IconTabWidget;
|
||||
|
||||
import android.content.Context;
|
||||
@ -23,7 +24,10 @@ import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.FrameLayout;
|
||||
@ -32,7 +36,8 @@ import android.widget.LinearLayout;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
public class TabsPanel extends LinearLayout
|
||||
implements LightweightTheme.OnChangeListener,
|
||||
implements GeckoPopupMenu.OnMenuItemClickListener,
|
||||
LightweightTheme.OnChangeListener,
|
||||
IconTabWidget.OnTabChangedListener {
|
||||
@SuppressWarnings("unused")
|
||||
private static final String LOGTAG = "Gecko" + TabsPanel.class.getSimpleName();
|
||||
@ -50,6 +55,10 @@ public class TabsPanel extends LinearLayout
|
||||
public boolean shouldExpand();
|
||||
}
|
||||
|
||||
public static interface CloseAllPanelView {
|
||||
public void closeAll();
|
||||
}
|
||||
|
||||
public static interface TabsLayoutChangeListener {
|
||||
public void onTabsLayoutChange(int width, int height);
|
||||
}
|
||||
@ -68,6 +77,7 @@ public class TabsPanel extends LinearLayout
|
||||
private AppStateListener mAppStateListener;
|
||||
|
||||
private IconTabWidget mTabWidget;
|
||||
private static ImageButton mMenuButton;
|
||||
private static ImageButton mAddTab;
|
||||
|
||||
private Panel mCurrentPanel;
|
||||
@ -75,6 +85,8 @@ public class TabsPanel extends LinearLayout
|
||||
private boolean mVisible;
|
||||
private boolean mHeaderVisible;
|
||||
|
||||
private GeckoPopupMenu mPopupMenu;
|
||||
|
||||
public TabsPanel(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
@ -91,6 +103,10 @@ public class TabsPanel extends LinearLayout
|
||||
|
||||
mIsSideBar = false;
|
||||
|
||||
mPopupMenu = new GeckoPopupMenu(context);
|
||||
mPopupMenu.inflate(R.menu.tabs_menu);
|
||||
mPopupMenu.setOnMenuItemClickListener(this);
|
||||
|
||||
LayoutInflater.from(context).inflate(R.layout.tabs_panel, this);
|
||||
initialize();
|
||||
|
||||
@ -149,6 +165,19 @@ public class TabsPanel extends LinearLayout
|
||||
}
|
||||
|
||||
mTabWidget.setTabSelectionListener(this);
|
||||
|
||||
mMenuButton = (ImageButton) findViewById(R.id.menu);
|
||||
mMenuButton.setOnClickListener(new Button.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
final Menu menu = mPopupMenu.getMenu();
|
||||
menu.findItem(R.id.close_all_tabs).setVisible(mCurrentPanel == Panel.NORMAL_TABS);
|
||||
menu.findItem(R.id.close_private_tabs).setVisible(mCurrentPanel == Panel.PRIVATE_TABS);
|
||||
|
||||
mPopupMenu.show();
|
||||
}
|
||||
});
|
||||
mPopupMenu.setAnchor(mMenuButton);
|
||||
}
|
||||
|
||||
public void addTab() {
|
||||
@ -172,6 +201,37 @@ public class TabsPanel extends LinearLayout
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onMenuItemClick(MenuItem item) {
|
||||
final int itemId = item.getItemId();
|
||||
|
||||
if (itemId == R.id.close_all_tabs) {
|
||||
if (mCurrentPanel == Panel.NORMAL_TABS) {
|
||||
// Disable the menu button so that the menu won't interfere with the tab close animation.
|
||||
mMenuButton.setEnabled(false);
|
||||
((CloseAllPanelView) mPanelNormal).closeAll();
|
||||
} else {
|
||||
Log.e(LOGTAG, "Close all tabs menu item should only be visible for normal tabs panel");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itemId == R.id.close_private_tabs) {
|
||||
if (mCurrentPanel == Panel.PRIVATE_TABS) {
|
||||
((CloseAllPanelView) mPanelPrivate).closeAll();
|
||||
} else {
|
||||
Log.e(LOGTAG, "Close private tabs menu item should only be visible for private tabs panel");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (itemId == R.id.new_tab || itemId == R.id.new_private_tab) {
|
||||
hide();
|
||||
}
|
||||
|
||||
return mActivity.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
private static int getTabContainerHeight(TabsListContainer listContainer) {
|
||||
Resources resources = listContainer.getContext().getResources();
|
||||
|
||||
@ -350,12 +410,17 @@ public class TabsPanel extends LinearLayout
|
||||
mFooter.setVisibility(View.GONE);
|
||||
|
||||
mAddTab.setVisibility(View.INVISIBLE);
|
||||
|
||||
mMenuButton.setVisibility(View.INVISIBLE);
|
||||
} else {
|
||||
if (mFooter != null)
|
||||
mFooter.setVisibility(View.VISIBLE);
|
||||
|
||||
mAddTab.setVisibility(View.VISIBLE);
|
||||
mAddTab.setImageLevel(index);
|
||||
|
||||
mMenuButton.setVisibility(View.VISIBLE);
|
||||
mMenuButton.setEnabled(true);
|
||||
}
|
||||
|
||||
if (isSideBar()) {
|
||||
@ -374,6 +439,7 @@ public class TabsPanel extends LinearLayout
|
||||
|
||||
if (mVisible) {
|
||||
mVisible = false;
|
||||
mPopupMenu.dismiss();
|
||||
dispatchLayoutChange(0, 0);
|
||||
}
|
||||
}
|
||||
|
@ -17,6 +17,7 @@ import org.mozilla.gecko.Tabs;
|
||||
import org.mozilla.gecko.animation.PropertyAnimator;
|
||||
import org.mozilla.gecko.animation.PropertyAnimator.Property;
|
||||
import org.mozilla.gecko.animation.ViewHelper;
|
||||
import org.mozilla.gecko.util.ThreadUtils;
|
||||
import org.mozilla.gecko.widget.TwoWayView;
|
||||
import org.mozilla.gecko.widget.TabThumbnailWrapper;
|
||||
|
||||
@ -38,38 +39,44 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
class TabsTray extends TwoWayView
|
||||
implements TabsPanel.PanelView {
|
||||
implements TabsPanel.PanelView,
|
||||
TabsPanel.CloseAllPanelView {
|
||||
private static final String LOGTAG = "Gecko" + TabsTray.class.getSimpleName();
|
||||
|
||||
private Context mContext;
|
||||
private TabsPanel mTabsPanel;
|
||||
|
||||
final private boolean mIsPrivate;
|
||||
|
||||
private TabsAdapter mTabsAdapter;
|
||||
|
||||
private List<View> mPendingClosedTabs;
|
||||
private int mCloseAnimationCount;
|
||||
private int mCloseAnimationCount = 0;
|
||||
private int mCloseAllAnimationCount = 0;
|
||||
|
||||
private TabSwipeGestureListener mSwipeListener;
|
||||
|
||||
// Time to animate non-flinged tabs of screen, in milliseconds
|
||||
private static final int ANIMATION_DURATION = 250;
|
||||
|
||||
// Time between starting successive tab animations in closeAllTabs.
|
||||
private static final int ANIMATION_CASCADE_DELAY = 75;
|
||||
|
||||
private int mOriginalSize = 0;
|
||||
|
||||
public TabsTray(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mContext = context;
|
||||
|
||||
mCloseAnimationCount = 0;
|
||||
mPendingClosedTabs = new ArrayList<View>();
|
||||
|
||||
setItemsCanFocus(true);
|
||||
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TabsTray);
|
||||
boolean isPrivate = (a.getInt(R.styleable.TabsTray_tabs, 0x0) == 1);
|
||||
mIsPrivate = (a.getInt(R.styleable.TabsTray_tabs, 0x0) == 1);
|
||||
a.recycle();
|
||||
|
||||
mTabsAdapter = new TabsAdapter(mContext, isPrivate);
|
||||
mTabsAdapter = new TabsAdapter(mContext);
|
||||
setAdapter(mTabsAdapter);
|
||||
|
||||
mSwipeListener = new TabSwipeGestureListener();
|
||||
@ -137,15 +144,13 @@ class TabsTray extends TwoWayView
|
||||
// Adapter to bind tabs into a list
|
||||
private class TabsAdapter extends BaseAdapter implements Tabs.OnTabsChangedListener {
|
||||
private Context mContext;
|
||||
private boolean mIsPrivate;
|
||||
private ArrayList<Tab> mTabs;
|
||||
private LayoutInflater mInflater;
|
||||
private Button.OnClickListener mOnCloseClickListener;
|
||||
|
||||
public TabsAdapter(Context context, boolean isPrivate) {
|
||||
public TabsAdapter(Context context) {
|
||||
mContext = context;
|
||||
mInflater = LayoutInflater.from(mContext);
|
||||
mIsPrivate = isPrivate;
|
||||
|
||||
mOnCloseClickListener = new Button.OnClickListener() {
|
||||
@Override
|
||||
@ -281,16 +286,21 @@ class TabsTray extends TwoWayView
|
||||
|
||||
private void resetTransforms(View view) {
|
||||
ViewHelper.setAlpha(view, 1);
|
||||
if (mOriginalSize == 0)
|
||||
return;
|
||||
|
||||
if (isVertical()) {
|
||||
ViewHelper.setHeight(view, mOriginalSize);
|
||||
ViewHelper.setTranslationX(view, 0);
|
||||
} else {
|
||||
ViewHelper.setWidth(view, mOriginalSize);
|
||||
ViewHelper.setTranslationY(view, 0);
|
||||
}
|
||||
|
||||
// We only need to reset the height or width after individual tab close animations.
|
||||
if (mOriginalSize != 0) {
|
||||
if (isVertical()) {
|
||||
ViewHelper.setHeight(view, mOriginalSize);
|
||||
} else {
|
||||
ViewHelper.setWidth(view, mOriginalSize);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -320,6 +330,75 @@ class TabsTray extends TwoWayView
|
||||
return (getOrientation().compareTo(TwoWayView.Orientation.VERTICAL) == 0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeAll() {
|
||||
final int childCount = getChildCount();
|
||||
|
||||
// Just close the panel if there are no tabs to close.
|
||||
if (childCount == 0) {
|
||||
autoHidePanel();
|
||||
return;
|
||||
}
|
||||
|
||||
// Disable the view so that gestures won't interfere wth the tab close animation.
|
||||
setEnabled(false);
|
||||
|
||||
// Delay starting each successive animation to create a cascade effect.
|
||||
int cascadeDelay = 0;
|
||||
|
||||
for (int i = childCount - 1; i >= 0; i--) {
|
||||
final View view = getChildAt(i);
|
||||
final PropertyAnimator animator = new PropertyAnimator(ANIMATION_DURATION);
|
||||
animator.attach(view, Property.ALPHA, 0);
|
||||
|
||||
if (isVertical()) {
|
||||
animator.attach(view, Property.TRANSLATION_X, view.getWidth());
|
||||
} else {
|
||||
animator.attach(view, Property.TRANSLATION_Y, view.getHeight());
|
||||
}
|
||||
|
||||
mCloseAllAnimationCount++;
|
||||
|
||||
animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
|
||||
@Override
|
||||
public void onPropertyAnimationStart() { }
|
||||
|
||||
@Override
|
||||
public void onPropertyAnimationEnd() {
|
||||
mCloseAllAnimationCount--;
|
||||
if (mCloseAllAnimationCount > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Hide the panel after the animation is done.
|
||||
autoHidePanel();
|
||||
|
||||
// Re-enable the view after the animation is done.
|
||||
TabsTray.this.setEnabled(true);
|
||||
|
||||
// Then actually close all the tabs.
|
||||
final Iterable<Tab> tabs = Tabs.getInstance().getTabsInOrder();
|
||||
for (Tab tab : tabs) {
|
||||
// In the normal panel we want to close all tabs (both private and normal),
|
||||
// but in the private panel we only want to close private tabs.
|
||||
if (!mIsPrivate || tab.isPrivate()) {
|
||||
Tabs.getInstance().closeTab(tab, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
ThreadUtils.getUiHandler().postDelayed(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
animator.start();
|
||||
}
|
||||
}, cascadeDelay);
|
||||
|
||||
cascadeDelay += ANIMATION_CASCADE_DELAY;
|
||||
}
|
||||
}
|
||||
|
||||
private void animateClose(final View view, int pos) {
|
||||
PropertyAnimator animator = new PropertyAnimator(ANIMATION_DURATION);
|
||||
animator.attach(view, Property.ALPHA, 0);
|
||||
@ -565,7 +644,7 @@ class TabsTray extends TwoWayView
|
||||
}
|
||||
|
||||
case MotionEvent.ACTION_MOVE: {
|
||||
if (mSwipeView == null)
|
||||
if (mSwipeView == null || mVelocityTracker == null)
|
||||
break;
|
||||
|
||||
mVelocityTracker.addMovement(e);
|
||||
|
Loading…
Reference in New Issue
Block a user