parent
0336487e34
commit
387011a1ff
|
|
@ -0,0 +1,57 @@
|
|||
/**
|
||||
* @file llbrowsernotification.cpp
|
||||
* @brief Notification Handler Class for browser popups
|
||||
*
|
||||
* $LicenseInfo:firstyear=2000&license=viewerlgpl$
|
||||
* Second Life Viewer Source Code
|
||||
* Copyright (C) 2010, Linden Research, Inc.
|
||||
*
|
||||
* This library is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License as published by the Free Software Foundation;
|
||||
* version 2.1 of the License only.
|
||||
*
|
||||
* This library is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public
|
||||
* License along with this library; if not, write to the Free Software
|
||||
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
*
|
||||
* Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
|
||||
* $/LicenseInfo$
|
||||
*/
|
||||
|
||||
|
||||
#include "llviewerprecompiledheaders.h" // must be first include
|
||||
|
||||
#include "llnotificationhandler.h"
|
||||
#include "llnotifications.h"
|
||||
#include "llfloatermediabrowser.h"
|
||||
#include "llfloaterreg.h"
|
||||
|
||||
using namespace LLNotificationsUI;
|
||||
|
||||
bool LLBrowserNotification::processNotification(const LLSD& notify)
|
||||
{
|
||||
LLNotificationPtr notification = LLNotifications::instance().find(notify["id"].asUUID());
|
||||
|
||||
if (notification)
|
||||
{
|
||||
LLFloaterMediaBrowser* browserp = dynamic_cast<LLFloaterMediaBrowser*>(LLFloaterReg::findInstance("media_browser", notification->getPayload()["source"]));
|
||||
if (notify["sigtype"].asString() == "add" || notify["sigtype"].asString() == "load")
|
||||
{
|
||||
if (browserp)
|
||||
{
|
||||
browserp->showNotification(notification);
|
||||
}
|
||||
}
|
||||
else if (notify["sigtype"].asString() == "delete")
|
||||
{
|
||||
browserp->hideNotification();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
@ -45,7 +45,10 @@
|
|||
#include "llviewermedia.h"
|
||||
#include "llviewerparcelmedia.h"
|
||||
#include "llcombobox.h"
|
||||
#include "lllayoutstack.h"
|
||||
#include "llcheckboxctrl.h"
|
||||
|
||||
#include "llnotifications.h"
|
||||
|
||||
// TEMP
|
||||
#include "llsdutil.h"
|
||||
|
|
@ -141,12 +144,16 @@ void LLFloaterMediaBrowser::draw()
|
|||
BOOL LLFloaterMediaBrowser::postBuild()
|
||||
{
|
||||
mBrowser = getChild<LLMediaCtrl>("browser");
|
||||
mBrowser->setMediaID(mKey);
|
||||
mBrowser->addObserver(this);
|
||||
|
||||
mAddressCombo = getChild<LLComboBox>("address");
|
||||
mAddressCombo->setCommitCallback(onEnterAddress, this);
|
||||
mAddressCombo->sortByName();
|
||||
|
||||
LLButton& notification_close = getChildRef<LLButton>("close_notification");
|
||||
notification_close.setClickedCallback(boost::bind(&LLFloaterMediaBrowser::onCloseNotification, this), NULL);
|
||||
|
||||
childSetAction("back", onClickBack, this);
|
||||
childSetAction("forward", onClickForward, this);
|
||||
childSetAction("reload", onClickRefresh, this);
|
||||
|
|
@ -243,6 +250,73 @@ void LLFloaterMediaBrowser::setCurrentURL(const std::string& url)
|
|||
getChildView("reload")->setEnabled(TRUE);
|
||||
}
|
||||
|
||||
void LLFloaterMediaBrowser::showNotification(LLNotificationPtr notify)
|
||||
{
|
||||
mCurNotification = notify;
|
||||
|
||||
// add popup here
|
||||
LLSD payload = notify->getPayload();
|
||||
|
||||
LLNotificationFormPtr formp = notify->getForm();
|
||||
LLLayoutPanel& panel = getChildRef<LLLayoutPanel>("notification_area");
|
||||
panel.setVisible(true);
|
||||
panel.getChild<LLUICtrl>("notification_icon")->setValue(notify->getIcon());
|
||||
panel.getChild<LLUICtrl>("notification_text")->setValue(notify->getMessage());
|
||||
panel.getChild<LLUICtrl>("notification_text")->setToolTip(notify->getMessage());
|
||||
LLNotificationForm::EIgnoreType ignore_type = formp->getIgnoreType();
|
||||
LLLayoutPanel& form_elements = panel.getChildRef<LLLayoutPanel>("form_elements");
|
||||
|
||||
const S32 FORM_PADDING_HORIZONTAL = 10;
|
||||
const S32 FORM_PADDING_VERTICAL = 5;
|
||||
S32 cur_x = FORM_PADDING_HORIZONTAL;
|
||||
|
||||
if (ignore_type != LLNotificationForm::IGNORE_NO)
|
||||
{
|
||||
LLCheckBoxCtrl::Params checkbox_p;
|
||||
checkbox_p.name = "ignore_check";
|
||||
checkbox_p.rect = LLRect(cur_x, form_elements.getRect().getHeight() - FORM_PADDING_VERTICAL, cur_x, FORM_PADDING_VERTICAL);
|
||||
checkbox_p.label = formp->getIgnoreMessage();
|
||||
checkbox_p.label_text.text_color = LLColor4::black;
|
||||
checkbox_p.commit_callback.function = boost::bind(&LLFloaterMediaBrowser::onClickIgnore, this, _1);
|
||||
|
||||
LLCheckBoxCtrl* check = LLUICtrlFactory::create<LLCheckBoxCtrl>(checkbox_p);
|
||||
check->setRect(check->getBoundingRect());
|
||||
form_elements.addChild(check);
|
||||
cur_x = check->getRect().mRight + FORM_PADDING_HORIZONTAL;
|
||||
}
|
||||
|
||||
for (S32 i = 0; i < formp->getNumElements(); i++)
|
||||
{
|
||||
LLSD form_element = formp->getElement(i);
|
||||
if (form_element["type"].asString() == "button")
|
||||
{
|
||||
LLButton::Params button_p;
|
||||
button_p.name = form_element["name"];
|
||||
button_p.label = form_element["text"];
|
||||
button_p.rect = LLRect(cur_x, form_elements.getRect().getHeight() - FORM_PADDING_VERTICAL, cur_x, FORM_PADDING_VERTICAL);
|
||||
button_p.commit_callback.function = boost::bind(&LLFloaterMediaBrowser::onClickNotificationButton, this, form_element["name"].asString());
|
||||
button_p.auto_resize = true;
|
||||
|
||||
LLButton* button = LLUICtrlFactory::create<LLButton>(button_p);
|
||||
button->autoResize();
|
||||
form_elements.addChild(button);
|
||||
|
||||
cur_x = button->getRect().mRight + FORM_PADDING_HORIZONTAL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
form_elements.reshape(cur_x, form_elements.getRect().getHeight());
|
||||
|
||||
//LLWeb::loadURL(payload["url"], payload["target"]);
|
||||
}
|
||||
|
||||
void LLFloaterMediaBrowser::hideNotification()
|
||||
{
|
||||
LLLayoutPanel& panel = getChildRef<LLLayoutPanel>("notification_area");
|
||||
panel.setVisible(FALSE);
|
||||
}
|
||||
|
||||
//static
|
||||
void LLFloaterMediaBrowser::onEnterAddress(LLUICtrl* ctrl, void* user_data)
|
||||
{
|
||||
|
|
@ -374,3 +448,29 @@ void LLFloaterMediaBrowser::openMedia(const std::string& media_url)
|
|||
mBrowser->navigateTo(media_url);
|
||||
setCurrentURL(media_url);
|
||||
}
|
||||
|
||||
void LLFloaterMediaBrowser::onCloseNotification()
|
||||
{
|
||||
LLNotifications::instance().cancel(mCurNotification);
|
||||
}
|
||||
|
||||
void LLFloaterMediaBrowser::onClickIgnore(LLUICtrl* ctrl)
|
||||
{
|
||||
bool check = ctrl->getValue().asBoolean();
|
||||
if (mCurNotification && mCurNotification->getForm()->getIgnoreType() == LLNotificationForm::IGNORE_SHOW_AGAIN)
|
||||
{
|
||||
// question was "show again" so invert value to get "ignore"
|
||||
check = !check;
|
||||
}
|
||||
mCurNotification->setIgnored(check);
|
||||
}
|
||||
|
||||
void LLFloaterMediaBrowser::onClickNotificationButton(const std::string& name)
|
||||
{
|
||||
if (!mCurNotification) return;
|
||||
|
||||
LLSD response = mCurNotification->getResponseTemplate();
|
||||
response[name] = true;
|
||||
|
||||
mCurNotification->respond(response);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@
|
|||
|
||||
class LLComboBox;
|
||||
class LLMediaCtrl;
|
||||
class LLNotification;
|
||||
|
||||
class LLFloaterMediaBrowser :
|
||||
public LLFloater,
|
||||
|
|
@ -55,6 +56,8 @@ public:
|
|||
void buildURLHistory();
|
||||
std::string getSupportURL();
|
||||
void setCurrentURL(const std::string& url);
|
||||
void showNotification(boost::shared_ptr<LLNotification> notify);
|
||||
void hideNotification();
|
||||
|
||||
static void onEnterAddress(LLUICtrl* ctrl, void* user_data);
|
||||
static void onClickRefresh(void* user_data);
|
||||
|
|
@ -70,9 +73,14 @@ public:
|
|||
static void onClickSeek(void* user_data);
|
||||
|
||||
private:
|
||||
void onCloseNotification();
|
||||
void onClickIgnore(LLUICtrl* ctrl);
|
||||
void onClickNotificationButton(const std::string& name);
|
||||
|
||||
LLMediaCtrl* mBrowser;
|
||||
LLComboBox* mAddressCombo;
|
||||
std::string mCurrentURL;
|
||||
boost::shared_ptr<LLNotification> mCurNotification;
|
||||
};
|
||||
|
||||
#endif // LL_LLFLOATERMEDIABROWSER_H
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@
|
|||
|
||||
// linden library includes
|
||||
#include "llfocusmgr.h"
|
||||
#include "llsdutil.h"
|
||||
|
||||
extern BOOL gRestoreGL;
|
||||
|
||||
|
|
@ -62,7 +63,8 @@ LLMediaCtrl::Params::Params()
|
|||
texture_width("texture_width", 1024),
|
||||
texture_height("texture_height", 1024),
|
||||
caret_color("caret_color"),
|
||||
initial_mime_type("initial_mime_type")
|
||||
initial_mime_type("initial_mime_type"),
|
||||
media_id("media_id")
|
||||
{
|
||||
tab_stop(false);
|
||||
}
|
||||
|
|
@ -88,6 +90,7 @@ LLMediaCtrl::LLMediaCtrl( const Params& p) :
|
|||
mTextureWidth ( 1024 ),
|
||||
mTextureHeight ( 1024 ),
|
||||
mClearCache(false),
|
||||
mMediaID(p.media_id),
|
||||
mHomePageMimeType(p.initial_mime_type)
|
||||
{
|
||||
{
|
||||
|
|
@ -924,9 +927,29 @@ void LLMediaCtrl::handleMediaEvent(LLPluginClassMedia* self, EMediaEvent event)
|
|||
case MEDIA_EVENT_CLICK_LINK_HREF:
|
||||
{
|
||||
LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, target is \"" << self->getClickTarget() << "\", uri is " << self->getClickURL() << LL_ENDL;
|
||||
// retrieve the event parameters
|
||||
std::string url = self->getClickURL();
|
||||
std::string target = self->getClickTarget();
|
||||
U32 target_type = self->getClickTargetType();
|
||||
|
||||
switch (target_type)
|
||||
{
|
||||
case LLPluginClassMedia::TARGET_NONE:
|
||||
// ignore this click and let media plugin handle it
|
||||
break;
|
||||
default:
|
||||
if(gSavedSettings.getBOOL("MediaEnablePopups"))
|
||||
{
|
||||
|
||||
LLNotificationsUtil::add("PopupAttempt",
|
||||
LLSD(),
|
||||
LLSD().with("source", mMediaID).with("target", target).with("url", url),
|
||||
boost::bind(&LLMediaCtrl::onPopup, this, _1, _2));
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
break;
|
||||
|
||||
|
||||
case MEDIA_EVENT_CLICK_LINK_NOFOLLOW:
|
||||
{
|
||||
LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_NOFOLLOW, uri is " << self->getClickURL() << LL_ENDL;
|
||||
|
|
@ -975,3 +998,10 @@ std::string LLMediaCtrl::getCurrentNavUrl()
|
|||
return mCurrentNavUrl;
|
||||
}
|
||||
|
||||
void LLMediaCtrl::onPopup(const LLSD& notification, const LLSD& response)
|
||||
{
|
||||
if (response["open"])
|
||||
{
|
||||
LLWeb::loadURL(notification["payload"]["url"], notification["payload"]["target"]);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ public:
|
|||
Optional<LLUIColor> caret_color;
|
||||
|
||||
Optional<std::string> initial_mime_type;
|
||||
Optional<std::string> media_id;
|
||||
|
||||
Params();
|
||||
};
|
||||
|
|
@ -139,6 +140,7 @@ public:
|
|||
bool getDecoupleTextureSize() { return mDecoupleTextureSize; }
|
||||
|
||||
void setTextureSize(S32 width, S32 height);
|
||||
void setMediaID(const std::string& id) { mMediaID = id; }
|
||||
|
||||
|
||||
// over-rides
|
||||
|
|
@ -161,6 +163,7 @@ public:
|
|||
|
||||
private:
|
||||
void onVisibilityChange ( const LLSD& new_visibility );
|
||||
void onPopup(const LLSD& notification, const LLSD& response);
|
||||
|
||||
const S32 mTextureDepthBytes;
|
||||
LLUUID mMediaTextureID;
|
||||
|
|
@ -182,6 +185,7 @@ public:
|
|||
bool mDecoupleTextureSize;
|
||||
S32 mTextureWidth;
|
||||
S32 mTextureHeight;
|
||||
std::string mMediaID;
|
||||
bool mClearCache;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -276,6 +276,15 @@ public:
|
|||
virtual bool processNotification(const LLSD& notify);
|
||||
};
|
||||
|
||||
/**
|
||||
* Handler for browser notifications
|
||||
*/
|
||||
class LLBrowserNotification : public LLSingleton<LLBrowserNotification>
|
||||
{
|
||||
public:
|
||||
virtual bool processNotification(const LLSD& notify);
|
||||
|
||||
};
|
||||
|
||||
class LLHandlerUtil
|
||||
{
|
||||
|
|
|
|||
|
|
@ -61,6 +61,7 @@ void LLNotificationManager::init()
|
|||
LLNotificationChannel::buildChannel("IM Notifications", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "notifytoast"));
|
||||
LLNotificationChannel::buildChannel("Offer", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "offer"));
|
||||
LLNotificationChannel::buildChannel("Hints", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "hint"));
|
||||
LLNotificationChannel::buildChannel("Browser", "Visible", LLNotificationFilters::filterBy<std::string>(&LLNotification::getType, "browser"));
|
||||
|
||||
LLNotifications::instance().getChannel("Notifications")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1));
|
||||
LLNotifications::instance().getChannel("NotificationTips")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1));
|
||||
|
|
@ -70,6 +71,7 @@ void LLNotificationManager::init()
|
|||
LLNotifications::instance().getChannel("IM Notifications")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1));
|
||||
LLNotifications::instance().getChannel("Offer")->connectChanged(boost::bind(&LLNotificationManager::onNotification, this, _1));
|
||||
LLNotifications::instance().getChannel("Hints")->connectChanged(boost::bind(&LLHintHandler::processNotification, LLHintHandler::getInstance(), _1));
|
||||
LLNotifications::instance().getChannel("Browser")->connectChanged(boost::bind(&LLBrowserNotification::processNotification, LLBrowserNotification::getInstance(), _1));
|
||||
|
||||
mNotifyHandlers["notify"] = boost::shared_ptr<LLEventHandler>(new LLScriptHandler(NT_NOTIFY, LLSD()));
|
||||
mNotifyHandlers["notifytip"] = boost::shared_ptr<LLEventHandler>(new LLTipHandler(NT_NOTIFY, LLSD()));
|
||||
|
|
|
|||
|
|
@ -2819,24 +2819,6 @@ void LLViewerMediaImpl::handleMediaEvent(LLPluginClassMedia* plugin, LLPluginCla
|
|||
case MEDIA_EVENT_CLICK_LINK_HREF:
|
||||
{
|
||||
LL_DEBUGS("Media") << "Media event: MEDIA_EVENT_CLICK_LINK_HREF, target is \"" << plugin->getClickTarget() << "\", uri is " << plugin->getClickURL() << LL_ENDL;
|
||||
// retrieve the event parameters
|
||||
std::string url = plugin->getClickURL();
|
||||
std::string target = plugin->getClickTarget();
|
||||
U32 target_type = plugin->getClickTargetType();
|
||||
|
||||
switch (target_type)
|
||||
{
|
||||
case LLPluginClassMedia::TARGET_NONE:
|
||||
// ignore this click and let media plugin handle it
|
||||
break;
|
||||
default:
|
||||
if(gSavedSettings.getBOOL("MediaEnablePopups"))
|
||||
{
|
||||
// loadURL now handles distinguishing between _blank, _external, and other named targets.
|
||||
LLWeb::loadURL(url, target);
|
||||
}
|
||||
break;
|
||||
}
|
||||
};
|
||||
break;
|
||||
case MEDIA_EVENT_PLUGIN_FAILED_LAUNCH:
|
||||
|
|
|
|||
|
|
@ -31,6 +31,7 @@
|
|||
width="800">
|
||||
<layout_panel
|
||||
auto_resize="false"
|
||||
default_tab_group="1"
|
||||
height="20"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
|
|
@ -78,6 +79,7 @@
|
|||
<combo_box
|
||||
allow_text_entry="true"
|
||||
follows="left|top|right"
|
||||
tab_group="1"
|
||||
height="20"
|
||||
layout="topleft"
|
||||
left_pad="5"
|
||||
|
|
@ -182,76 +184,6 @@
|
|||
function="MediaBrowser.Assign" />
|
||||
</button>
|
||||
</layout_panel>
|
||||
<layout_panel
|
||||
height="30"
|
||||
layout="topleft"
|
||||
name="notification_area"
|
||||
visible="false"
|
||||
user_resize="false"
|
||||
background_visible="true"
|
||||
bg_alpha_image="Yellow_Gradient"
|
||||
auto_resize="false"
|
||||
width="800">
|
||||
<layout_stack
|
||||
top="0"
|
||||
height="30"
|
||||
width="800"
|
||||
left="0"
|
||||
follows="all"
|
||||
orientation="horizontal"
|
||||
>
|
||||
<layout_panel
|
||||
height="30">
|
||||
<icon value="Popup_Caution"
|
||||
left="5"
|
||||
top="7"
|
||||
width="16"
|
||||
height="15"/>
|
||||
<text left_pad="8"
|
||||
top="10"
|
||||
height="25"
|
||||
width="400"
|
||||
text_color="black"
|
||||
font="SansSerifSmall"
|
||||
font.style="BOLD"
|
||||
name="notification_text"
|
||||
value="Notification text here"
|
||||
/>
|
||||
</layout_panel>
|
||||
<layout_panel
|
||||
height="30"
|
||||
width="270"
|
||||
auto_resize="false"
|
||||
name="form_elements">
|
||||
<check_box name="enable_check"
|
||||
left="5"
|
||||
top="5"
|
||||
height="20"
|
||||
width="120"
|
||||
label="Enable all popups"
|
||||
text_enabled_color="black"
|
||||
/>
|
||||
<button left_pad="5"
|
||||
width="140"
|
||||
top="4"
|
||||
label="Open pop-up window"/>
|
||||
</layout_panel>
|
||||
<layout_panel
|
||||
height="30"
|
||||
width="25"
|
||||
auto_resize="false"
|
||||
name="close_panel">
|
||||
<button left="5"
|
||||
name="close"
|
||||
width="16"
|
||||
height="16"
|
||||
top="8"
|
||||
image_color="DkGray_66"
|
||||
image_unselected="Icon_Close_Foreground"
|
||||
image_selected="Icon_Close_Press"/>
|
||||
</layout_panel>
|
||||
</layout_stack>
|
||||
</layout_panel>
|
||||
<layout_panel
|
||||
height="40"
|
||||
layout="topleft"
|
||||
|
|
@ -260,14 +192,84 @@
|
|||
top_delta="0"
|
||||
user_resize="false"
|
||||
width="540">
|
||||
<web_browser
|
||||
<web_browser
|
||||
bottom="-30"
|
||||
follows="left|right|top|bottom"
|
||||
follows="all"
|
||||
layout="topleft"
|
||||
left="0"
|
||||
name="browser"
|
||||
top="0"
|
||||
width="540" />
|
||||
<layout_stack
|
||||
name="notification_stack"
|
||||
left="0"
|
||||
top="0"
|
||||
width="540"
|
||||
bottom="-30"
|
||||
follows="all"
|
||||
mouse_opaque="false"
|
||||
orientation="vertical">
|
||||
<layout_panel
|
||||
height="30"
|
||||
min_height="30"
|
||||
layout="topleft"
|
||||
name="notification_area"
|
||||
visible="false"
|
||||
user_resize="false"
|
||||
background_visible="true"
|
||||
bg_alpha_image="Yellow_Gradient"
|
||||
auto_resize="false"
|
||||
width="800">
|
||||
<layout_stack
|
||||
top="0"
|
||||
height="30"
|
||||
width="800"
|
||||
left="0"
|
||||
follows="bottom|left|right"
|
||||
orientation="horizontal">
|
||||
<layout_panel
|
||||
height="30">
|
||||
<icon name="notification_icon"
|
||||
left="5"
|
||||
top="7"
|
||||
width="16"
|
||||
height="15"/>
|
||||
<text left_pad="8"
|
||||
top="10"
|
||||
height="25"
|
||||
width="400"
|
||||
text_color="black"
|
||||
font="SansSerifSmall"
|
||||
font.style="BOLD"
|
||||
name="notification_text"
|
||||
value="Notification text here"/>
|
||||
</layout_panel>
|
||||
<layout_panel
|
||||
height="30"
|
||||
width="130"
|
||||
auto_resize="false"
|
||||
user_resize="false"
|
||||
name="form_elements"/>
|
||||
<layout_panel
|
||||
height="30"
|
||||
width="25"
|
||||
auto_resize="false"
|
||||
name="close_panel">
|
||||
<button left="5"
|
||||
name="close_notification"
|
||||
width="16"
|
||||
height="16"
|
||||
top="8"
|
||||
image_color="DkGray_66"
|
||||
image_unselected="Icon_Close_Foreground"
|
||||
image_selected="Icon_Close_Press"/>
|
||||
</layout_panel>
|
||||
</layout_stack>
|
||||
</layout_panel>
|
||||
<layout_panel
|
||||
mouse_opaque="false"
|
||||
auto_resize="true"/>
|
||||
</layout_stack>
|
||||
<button
|
||||
follows="bottom|left"
|
||||
height="20"
|
||||
|
|
|
|||
Loading…
Reference in New Issue