summaryrefslogtreecommitdiff
path: root/indra/newview
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview')
-rw-r--r--indra/newview/llbottomtray.cpp3
-rw-r--r--indra/newview/llchiclet.cpp109
-rw-r--r--indra/newview/llchiclet.h59
-rw-r--r--indra/newview/llfloatercamera.cpp6
-rw-r--r--indra/newview/llfloatercamera.h1
-rw-r--r--indra/newview/llfolderview.cpp4
-rw-r--r--indra/newview/llfolderviewitem.h1
-rw-r--r--indra/newview/llinventorybridge.cpp6
-rw-r--r--indra/newview/llpanellandmarks.cpp99
-rw-r--r--indra/newview/llpanellandmarks.h9
-rw-r--r--indra/newview/llpanelpicks.cpp12
-rw-r--r--indra/newview/llpanelprofile.cpp3
-rw-r--r--indra/newview/llpanelprofileview.cpp5
-rw-r--r--indra/newview/skins/default/xui/en/menu_places_gear_folder.xml9
-rw-r--r--indra/newview/skins/default/xui/en/menu_places_gear_landmark.xml9
15 files changed, 324 insertions, 11 deletions
diff --git a/indra/newview/llbottomtray.cpp b/indra/newview/llbottomtray.cpp
index 8987f14e97..da84303863 100644
--- a/indra/newview/llbottomtray.cpp
+++ b/indra/newview/llbottomtray.cpp
@@ -134,8 +134,9 @@ LLIMChiclet* LLBottomTray::createIMChiclet(const LLUUID& session_id)
case LLIMChiclet::TYPE_IM:
return getChicletPanel()->createChiclet<LLIMP2PChiclet>(session_id);
case LLIMChiclet::TYPE_GROUP:
- case LLIMChiclet::TYPE_AD_HOC:
return getChicletPanel()->createChiclet<LLIMGroupChiclet>(session_id);
+ case LLIMChiclet::TYPE_AD_HOC:
+ return getChicletPanel()->createChiclet<LLAdHocChiclet>(session_id);
case LLIMChiclet::TYPE_UNKNOWN:
break;
}
diff --git a/indra/newview/llchiclet.cpp b/indra/newview/llchiclet.cpp
index 340b0fa22c..69fa5cdfe7 100644
--- a/indra/newview/llchiclet.cpp
+++ b/indra/newview/llchiclet.cpp
@@ -468,6 +468,115 @@ void LLIMP2PChiclet::setShowSpeaker(bool show)
//////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////
+LLAdHocChiclet::Params::Params()
+: avatar_icon("avatar_icon")
+, unread_notifications("unread_notifications")
+, speaker("speaker")
+, show_speaker("show_speaker")
+{
+ // *TODO Vadim: Get rid of hardcoded values.
+ rect(LLRect(0, 25, 45, 0));
+
+ avatar_icon.name("avatar_icon");
+ avatar_icon.follows.flags(FOLLOWS_LEFT | FOLLOWS_TOP | FOLLOWS_BOTTOM);
+
+ // *NOTE dzaporozhan
+ // Changed icon height from 25 to 24 to fix ticket EXT-794.
+ // In some cases(after changing UI scale) 25 pixel height icon was
+ // drawn incorrectly, i'm not sure why.
+ avatar_icon.rect(LLRect(0, 24, 25, 0));
+ avatar_icon.mouse_opaque(false);
+
+ unread_notifications.name("unread");
+ unread_notifications.rect(LLRect(25, 25, 45, 0));
+ unread_notifications.font(LLFontGL::getFontSansSerif());
+ unread_notifications.font_halign(LLFontGL::HCENTER);
+ unread_notifications.v_pad(5);
+ unread_notifications.text_color(LLColor4::white);
+ unread_notifications.mouse_opaque(false);
+
+ speaker.name("speaker");
+ speaker.rect(LLRect(45, 25, 65, 0));
+
+ show_speaker = false;
+}
+
+LLAdHocChiclet::LLAdHocChiclet(const Params& p)
+: LLIMChiclet(p)
+, mChicletIconCtrl(NULL)
+, mCounterCtrl(NULL)
+, mSpeakerCtrl(NULL)
+, mPopupMenu(NULL)
+{
+ LLChicletAvatarIconCtrl::Params avatar_params = p.avatar_icon;
+ mChicletIconCtrl = LLUICtrlFactory::create<LLChicletAvatarIconCtrl>(avatar_params);
+ addChild(mChicletIconCtrl);
+
+ LLChicletNotificationCounterCtrl::Params unread_params = p.unread_notifications;
+ mCounterCtrl = LLUICtrlFactory::create<LLChicletNotificationCounterCtrl>(unread_params);
+ addChild(mCounterCtrl);
+
+ setCounter(getCounter());
+ setShowCounter(getShowCounter());
+
+ LLChicletSpeakerCtrl::Params speaker_params = p.speaker;
+ mSpeakerCtrl = LLUICtrlFactory::create<LLChicletSpeakerCtrl>(speaker_params);
+ addChild(mSpeakerCtrl);
+
+ setShowSpeaker(p.show_speaker);
+}
+
+void LLAdHocChiclet::setSessionId(const LLUUID& session_id)
+{
+ LLChiclet::setSessionId(session_id);
+ LLIMModel::LLIMSession* im_session = LLIMModel::getInstance()->findIMSession(session_id);
+ mChicletIconCtrl->setValue(im_session->mOtherParticipantID);
+}
+
+void LLAdHocChiclet::setCounter(S32 counter)
+{
+ mCounterCtrl->setCounter(counter);
+
+ if(getShowCounter())
+ {
+ LLRect counter_rect = mCounterCtrl->getRect();
+ LLRect required_rect = mCounterCtrl->getRequiredRect();
+ bool needs_resize = required_rect.getWidth() != counter_rect.getWidth();
+
+ if(needs_resize)
+ {
+ counter_rect.mRight = counter_rect.mLeft + required_rect.getWidth();
+ mCounterCtrl->reshape(counter_rect.getWidth(), counter_rect.getHeight());
+ mCounterCtrl->setRect(counter_rect);
+
+ onChicletSizeChanged();
+ }
+ }
+}
+
+LLRect LLAdHocChiclet::getRequiredRect()
+{
+ LLRect rect(0, 0, mChicletIconCtrl->getRect().getWidth(), 0);
+ if(getShowCounter())
+ {
+ rect.mRight += mCounterCtrl->getRequiredRect().getWidth();
+ }
+ if(getShowSpeaker())
+ {
+ rect.mRight += mSpeakerCtrl->getRect().getWidth();
+ }
+ return rect;
+}
+
+BOOL LLAdHocChiclet::handleRightMouseDown(S32 x, S32 y, MASK mask)
+{
+ return TRUE;
+}
+
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+//////////////////////////////////////////////////////////////////////////
+
LLIMGroupChiclet::Params::Params()
: group_icon("group_icon")
{
diff --git a/indra/newview/llchiclet.h b/indra/newview/llchiclet.h
index 458bc73bc4..932e05d95a 100644
--- a/indra/newview/llchiclet.h
+++ b/indra/newview/llchiclet.h
@@ -442,6 +442,65 @@ private:
};
/**
+ * Implements AD-HOC chiclet.
+ */
+class LLAdHocChiclet : public LLIMChiclet
+{
+public:
+ struct Params : public LLInitParam::Block<Params, LLChiclet::Params>
+ {
+ Optional<LLChicletAvatarIconCtrl::Params> avatar_icon;
+
+ Optional<LLChicletNotificationCounterCtrl::Params> unread_notifications;
+
+ Optional<LLChicletSpeakerCtrl::Params> speaker;
+
+ Optional<bool> show_speaker;
+
+ Params();
+ };
+
+ /**
+ * Sets session id.
+ * Session ID for group chat is actually Group ID.
+ */
+ /*virtual*/ void setSessionId(const LLUUID& session_id);
+
+ /*
+ * Sets number of unread messages. Will update chiclet's width if number text
+ * exceeds size of counter and notify it's parent about size change.
+ */
+ /*virtual*/ void setCounter(S32);
+
+ /*
+ * Returns number of unread messages.
+ */
+ /*virtual*/ S32 getCounter() { return mCounterCtrl->getCounter(); }
+
+ /*
+ * Returns rect, required to display chiclet.
+ * Width is the only valid value.
+ */
+ /*virtual*/ LLRect getRequiredRect();
+
+protected:
+ LLAdHocChiclet(const Params& p);
+ friend class LLUICtrlFactory;
+
+ /*
+ * Displays popup menu.
+ */
+ virtual BOOL handleRightMouseDown(S32 x, S32 y, MASK mask);
+
+private:
+
+ LLChicletAvatarIconCtrl* mChicletIconCtrl;
+ LLChicletNotificationCounterCtrl* mCounterCtrl;
+ LLChicletSpeakerCtrl* mSpeakerCtrl;
+ LLMenuGL* mPopupMenu;
+};
+
+/**
* Implements Group chat chiclet.
*/
class LLIMGroupChiclet : public LLIMChiclet, public LLGroupMgrObserver
diff --git a/indra/newview/llfloatercamera.cpp b/indra/newview/llfloatercamera.cpp
index db20b11efd..551853592b 100644
--- a/indra/newview/llfloatercamera.cpp
+++ b/indra/newview/llfloatercamera.cpp
@@ -125,6 +125,12 @@ void LLFloaterCamera::onOpen(const LLSD& key)
}
+void LLFloaterCamera::onClose(bool app_quitting)
+{
+ //We don't care of camera mode if app is quitting
+ if(!app_quitting)
+ switchMode(CAMERA_CTRL_MODE_ORBIT);
+}
LLFloaterCamera::LLFloaterCamera(const LLSD& val)
: LLDockableFloater(NULL, val),
diff --git a/indra/newview/llfloatercamera.h b/indra/newview/llfloatercamera.h
index 69df861a20..30a6b552f2 100644
--- a/indra/newview/llfloatercamera.h
+++ b/indra/newview/llfloatercamera.h
@@ -69,6 +69,7 @@ public:
static void updateIfNotInAvatarViewMode();
virtual void onOpen(const LLSD& key);
+ virtual void onClose(bool app_quitting);
LLJoystickCameraRotate* mRotate;
LLJoystickCameraZoom* mZoom;
diff --git a/indra/newview/llfolderview.cpp b/indra/newview/llfolderview.cpp
index 155262ee13..91f72fe7d3 100644
--- a/indra/newview/llfolderview.cpp
+++ b/indra/newview/llfolderview.cpp
@@ -1240,7 +1240,9 @@ BOOL LLFolderView::canCut() const
{
const LLFolderViewItem* item = *selected_it;
const LLFolderViewEventListener* listener = item->getListener();
- if (!listener || !listener->isItemMovable())
+
+ // *WARKAROUND: it is too many places where the "isItemRemovable" method should be changed with "const" modifier
+ if (!listener || !(const_cast<LLFolderViewEventListener*>(listener))->isItemRemovable())
{
return FALSE;
}
diff --git a/indra/newview/llfolderviewitem.h b/indra/newview/llfolderviewitem.h
index 90c346b381..62a4b9a187 100644
--- a/indra/newview/llfolderviewitem.h
+++ b/indra/newview/llfolderviewitem.h
@@ -202,6 +202,7 @@ public:
virtual S32 arrange( S32* width, S32* height, S32 filter_generation );
virtual S32 getItemHeight();
void setDontShowInHierarchy(bool dont_show) { mDontShowInHierarhy = dont_show; }
+ bool getDontShowInHierarchy() { return mDontShowInHierarhy; }
// applies filters to control visibility of inventory items
virtual void filter( LLInventoryFilter& filter);
diff --git a/indra/newview/llinventorybridge.cpp b/indra/newview/llinventorybridge.cpp
index db7d4f4c8f..b539715055 100644
--- a/indra/newview/llinventorybridge.cpp
+++ b/indra/newview/llinventorybridge.cpp
@@ -1547,6 +1547,12 @@ BOOL LLFolderBridge::dragCategoryIntoFolder(LLInventoryCategory* inv_cat,
// BAP - restrictions?
is_movable = true;
}
+
+ if (mUUID == gInventory.findCategoryUUIDForType(LLAssetType::AT_FAVORITE))
+ {
+ is_movable = FALSE; // It's generally movable but not into Favorites folder. EXT-1604
+ }
+
if( is_movable )
{
gInventory.collectDescendents( cat_id, descendent_categories, descendent_items, FALSE );
diff --git a/indra/newview/llpanellandmarks.cpp b/indra/newview/llpanellandmarks.cpp
index 2cbd80a72d..9f029e24e7 100644
--- a/indra/newview/llpanellandmarks.cpp
+++ b/indra/newview/llpanellandmarks.cpp
@@ -213,6 +213,17 @@ bool LLLandmarksPanel::isLandmarkSelected() const
return false;
}
+bool LLLandmarksPanel::isReceivedFolderSelected() const
+{
+ // Received Folder can be only in Landmarks accordion
+ if (mCurrentSelectedList != mLandmarksInventoryPanel) return false;
+
+ // *TODO: it should be filled with logic when EXT-976 is done.
+
+ llwarns << "Not implemented yet until EXT-976 is done." << llendl;
+
+ return false;
+}
LLLandmark* LLLandmarksPanel::getCurSelectedLandmark() const
{
@@ -612,13 +623,19 @@ void LLLandmarksPanel::onFoldingAction(const LLSD& userdata)
bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const
{
std::string command_name = userdata.asString();
+
+ LLPlacesFolderView* rootFolderView = mCurrentSelectedList ?
+ static_cast<LLPlacesFolderView*>(mCurrentSelectedList->getRootFolder()) : NULL;
+
+ if (NULL == rootFolderView) return false;
+
if("category" == command_name)
{
return mCurrentSelectedList == mLandmarksInventoryPanel;
}
- else if("paste" == command_name)
+ else if("paste" == command_name || "rename" == command_name || "cut" == command_name || "delete" == command_name)
{
- return mCurrentSelectedList ? mCurrentSelectedList->getRootFolder()->canPaste() : false;
+ return canSelectedBeModified(command_name);
}
else if ( "sort_by_date" == command_name)
{
@@ -627,14 +644,17 @@ bool LLLandmarksPanel::isActionEnabled(const LLSD& userdata) const
// do not allow teleport and more info for multi-selections
else if ("teleport" == command_name || "more_info" == command_name)
{
- return mCurrentSelectedList ?
- static_cast<LLPlacesFolderView*>(mCurrentSelectedList->getRootFolder())->getSelectedCount() == 1 : false;
+ return rootFolderView->getSelectedCount() == 1;
}
// we can add folder, or change item/folder only in Landmarks Accordion
- else if ("add_folder" == command_name || "rename" == command_name || "delete" == command_name)
+ else if ("add_folder" == command_name)
{
return mLandmarksInventoryPanel == mCurrentSelectedList;
}
+ else
+ {
+ llwarns << "Unprocessed command has come: " << command_name << llendl;
+ }
return true;
}
@@ -698,6 +718,75 @@ void LLLandmarksPanel::onCustomAction(const LLSD& userdata)
}
}
+/*
+Processes such actions: cut/rename/delete/paste actions
+
+Rules:
+ 1. We can't perform any action in Library
+ 2. For Landmarks we can:
+ - cut/rename/delete in any other accordions
+ - paste - only in Favorites, Landmarks accordions
+ 3. For Folders we can: perform any action in Landmarks accordion, except Received folder
+ 4. We can not paste folders from Clipboard (processed by LLFolderView::canPaste())
+ 5. Check LLFolderView/Inventory Bridges rules
+ */
+bool LLLandmarksPanel::canSelectedBeModified(const std::string& command_name) const
+{
+ // validate own rules first
+
+ // nothing can be modified in Library
+ if (mLibraryInventoryPanel == mCurrentSelectedList) return false;
+
+ bool can_be_modified = false;
+
+ // landmarks can be modified in any other accordion...
+ if (isLandmarkSelected())
+ {
+ can_be_modified = true;
+
+ // we can modify landmarks anywhere except paste to My Inventory
+ if ("paste" == command_name)
+ {
+ can_be_modified = (mCurrentSelectedList != mMyInventoryPanel);
+ }
+ }
+ else
+ {
+ // ...folders only in the Landmarks accordion...
+ can_be_modified = mLandmarksInventoryPanel == mCurrentSelectedList;
+
+ // ...except "Received" folder
+ can_be_modified &= !isReceivedFolderSelected();
+ }
+
+ // then ask LLFolderView permissions
+ if (can_be_modified)
+ {
+ if ("cut" == command_name)
+ {
+ can_be_modified = mCurrentSelectedList->getRootFolder()->canCut();
+ }
+ else if ("rename" == command_name)
+ {
+ can_be_modified = getCurSelectedItem()->getListener()->isItemRenameable();
+ }
+ else if ("delete" == command_name)
+ {
+ can_be_modified = getCurSelectedItem()->getListener()->isItemRemovable();
+ }
+ else if("paste" == command_name)
+ {
+ return mCurrentSelectedList->getRootFolder()->canPaste();
+ }
+ else
+ {
+ llwarns << "Unprocessed command has come: " << command_name << llendl;
+ }
+ }
+
+ return can_be_modified;
+}
+
void LLLandmarksPanel::onPickPanelExit( LLPanelPickEdit* pick_panel, LLView* owner, const LLSD& params)
{
pick_panel->setVisible(FALSE);
diff --git a/indra/newview/llpanellandmarks.h b/indra/newview/llpanellandmarks.h
index 389a04a76f..52ad317afe 100644
--- a/indra/newview/llpanellandmarks.h
+++ b/indra/newview/llpanellandmarks.h
@@ -66,6 +66,7 @@ protected:
* @return true - if current selected panel is not null and selected item is a landmark
*/
bool isLandmarkSelected() const;
+ bool isReceivedFolderSelected() const;
LLLandmark* getCurSelectedLandmark() const;
LLFolderViewItem* getCurSelectedItem () const;
void updateSortOrder(LLInventoryPanel* panel, bool byDate);
@@ -97,6 +98,14 @@ private:
void onFoldingAction(const LLSD& command_name);
bool isActionEnabled(const LLSD& command_name) const;
void onCustomAction(const LLSD& command_name);
+
+ /**
+ * Determines if selected item can be modified via context/gear menu.
+ *
+ * It validates Places Landmarks rules first. And then LLFolderView permissions.
+ * For now it checks cut/rename/delete/paste actions.
+ */
+ bool canSelectedBeModified(const std::string& command_name) const;
void onPickPanelExit( LLPanelPickEdit* pick_panel, LLView* owner, const LLSD& params);
private:
diff --git a/indra/newview/llpanelpicks.cpp b/indra/newview/llpanelpicks.cpp
index 073da5cc06..d6040c497c 100644
--- a/indra/newview/llpanelpicks.cpp
+++ b/indra/newview/llpanelpicks.cpp
@@ -91,7 +91,12 @@ void* LLPanelPicks::create(void* data /* = NULL */)
void LLPanelPicks::updateData()
{
- LLAvatarPropertiesProcessor::getInstance()->sendAvatarPicksRequest(getAvatarId());
+ // Send Picks request only when we need to, not on every onOpen(during tab switch).
+ if(isDirty())
+ {
+ mPicksList->clear();
+ LLAvatarPropertiesProcessor::getInstance()->sendAvatarPicksRequest(getAvatarId());
+ }
}
void LLPanelPicks::processProperties(void* data, EAvatarProcessorType type)
@@ -134,6 +139,7 @@ void LLPanelPicks::processProperties(void* data, EAvatarProcessorType type)
picture->setMouseUpCallback(boost::bind(&LLPanelPicks::updateButtons, this));
}
+ resetDirty();
LLAvatarPropertiesProcessor::getInstance()->removeObserver(getAvatarId(),this);
updateButtons();
}
@@ -183,8 +189,6 @@ void LLPanelPicks::onOpen(const LLSD& key)
// Disable buttons when viewing profile for first time
if(getAvatarId() != id)
{
- clear();
-
childSetEnabled(XML_BTN_INFO,FALSE);
childSetEnabled(XML_BTN_TELEPORT,FALSE);
childSetEnabled(XML_BTN_SHOW_ON_MAP,FALSE);
@@ -204,6 +208,8 @@ void LLPanelPicks::onOpen(const LLSD& key)
if(getAvatarId() != id)
{
mPicksList->goToTop();
+ // Set dummy value to make panel dirty and make it reload picks
+ setValue(LLSD());
}
LLPanelProfileTab::onOpen(key);
diff --git a/indra/newview/llpanelprofile.cpp b/indra/newview/llpanelprofile.cpp
index 08d2baf6cd..bec670cdaa 100644
--- a/indra/newview/llpanelprofile.cpp
+++ b/indra/newview/llpanelprofile.cpp
@@ -192,8 +192,9 @@ void LLPanelProfile::openPanel(LLPanel* panel, const LLSD& params)
void LLPanelProfile::notifyParent(const LLSD& info)
{
+ std::string action = info["action"];
// lets update Picks list after Pick was saved
- if("save_new_pick" == info["action"])
+ if("save_new_pick" == action)
{
onOpen(info);
return;
diff --git a/indra/newview/llpanelprofileview.cpp b/indra/newview/llpanelprofileview.cpp
index f1db2416e4..1d16c4ef5e 100644
--- a/indra/newview/llpanelprofileview.cpp
+++ b/indra/newview/llpanelprofileview.cpp
@@ -46,6 +46,7 @@ static LLRegisterPanelClassWrapper<LLPanelProfileView> t_panel_target_profile("p
static std::string PANEL_NOTES = "panel_notes";
static const std::string PANEL_PROFILE = "panel_profile";
+static const std::string PANEL_PICKS = "panel_picks";
LLPanelProfileView::LLPanelProfileView()
: LLPanelProfile()
@@ -103,6 +104,10 @@ BOOL LLPanelProfileView::postBuild()
void LLPanelProfileView::onBackBtnClick()
{
+ // Set dummy value to make picks panel dirty,
+ // This will make Picks reload on next open.
+ getTabContainer()[PANEL_PICKS]->setValue(LLSD());
+
LLSideTrayPanelContainer* parent = dynamic_cast<LLSideTrayPanelContainer*>(getParent());
if(parent)
{
diff --git a/indra/newview/skins/default/xui/en/menu_places_gear_folder.xml b/indra/newview/skins/default/xui/en/menu_places_gear_folder.xml
index c95cf32a5a..29fb4990d0 100644
--- a/indra/newview/skins/default/xui/en/menu_places_gear_folder.xml
+++ b/indra/newview/skins/default/xui/en/menu_places_gear_folder.xml
@@ -34,6 +34,9 @@
<on_click
function="Places.LandmarksGear.CopyPaste.Action"
parameter="cut" />
+ <on_enable
+ function="Places.LandmarksGear.Enable"
+ parameter="cut" />
</menu_item_call>
<menu_item_call
label="Copy"
@@ -61,6 +64,9 @@
<on_click
function="Places.LandmarksGear.CopyPaste.Action"
parameter="rename" />
+ <on_enable
+ function="Places.LandmarksGear.Enable"
+ parameter="rename" />
</menu_item_call>
<menu_item_call
label="Delete"
@@ -69,6 +75,9 @@
<on_click
function="Places.LandmarksGear.CopyPaste.Action"
parameter="delete" />
+ <on_enable
+ function="Places.LandmarksGear.Enable"
+ parameter="delete" />
</menu_item_call>
<menu_item_separator
layout="topleft" />
diff --git a/indra/newview/skins/default/xui/en/menu_places_gear_landmark.xml b/indra/newview/skins/default/xui/en/menu_places_gear_landmark.xml
index 0246d775ee..9d2d162270 100644
--- a/indra/newview/skins/default/xui/en/menu_places_gear_landmark.xml
+++ b/indra/newview/skins/default/xui/en/menu_places_gear_landmark.xml
@@ -66,6 +66,9 @@
<on_click
function="Places.LandmarksGear.CopyPaste.Action"
parameter="cut" />
+ <on_enable
+ function="Places.LandmarksGear.Enable"
+ parameter="cut" />
</menu_item_call>
<menu_item_call
label="Copy Landmark"
@@ -101,6 +104,9 @@
<on_click
function="Places.LandmarksGear.CopyPaste.Action"
parameter="rename" />
+ <on_enable
+ function="Places.LandmarksGear.Enable"
+ parameter="rename" />
</menu_item_call>
<menu_item_call
label="Delete"
@@ -109,6 +115,9 @@
<on_click
function="Places.LandmarksGear.CopyPaste.Action"
parameter="delete" />
+ <on_enable
+ function="Places.LandmarksGear.Enable"
+ parameter="delete" />
</menu_item_call>
<menu_item_separator
layout="topleft" />