diff --git a/modules/core_lib/widgets/uiscrollarea.lua b/modules/core_lib/widgets/uiscrollarea.lua index 1db2550a..e7e47737 100644 --- a/modules/core_lib/widgets/uiscrollarea.lua +++ b/modules/core_lib/widgets/uiscrollarea.lua @@ -1,15 +1,74 @@ UIScrollArea = extends(UIWidget) +-- public functions function UIScrollArea.create() local scrollarea = UIScrollArea.internalCreate() scrollarea:setClipping(true) + scrollarea.inverted = false return scrollarea end function UIScrollArea:onStyleApply(styleName, styleNode) for name,value in pairs(styleNode) do - if name == 'horizontal-scrollbar' then - + if name == 'vertical-scrollbar' then + addEvent(function() + self:setVerticalScrollBar(self:getParent():getChildById(value)) + end) + elseif name == 'horizontal-scrollbar' then + addEvent(function() + self:setHorizontalScrollBar(self:getParent():getChildById(value)) + end) + elseif name == 'inverted-scroll' then + self:setInverted(value) end end end + +function UIScrollArea:updateScrollBars() + local offset = { x = 0, y = 0 } + local scrollheight = math.max(self:getChildrenRect().height - self:getClippingRect().height, 0) + local scrollwidth = math.max(self:getChildrenRect().width - self:getClippingRect().width, 0) + + local scrollbar = self.verticalScrollBar + if scrollbar then + if self.inverted then + scrollbar:setMinimum(-scrollheight) + else + scrollbar:setMaximum(scrollheight) + end + end + + local scrollbar = self.horizontalScrollBar + if scrollbar then + if self.inverted then + scrollbar:setMinimum(-scrollwidth) + else + scrollbar:setMaximum(scrollwidth) + end + end +end + +function UIScrollArea:setVerticalScrollBar(scrollbar) + self.verticalScrollBar = scrollbar + scrollbar:setMaximum(0) + self.verticalScrollBar.onValueChange = function(scrollbar, value) + local virtualOffset = self:getVirtualOffset() + if self.inverted then value = -value end + virtualOffset.y = value + self:setVirtualOffset(virtualOffset) + end + self:updateScrollBars() +end + +function UIScrollArea:setHorizontalScrollBar(scrollbar) + self.horizontalScrollBar = scrollbar + self:updateScrollBars() +end + +function UIScrollArea:setInverted(inverted) + self.inverted = inverted +end + +function UIScrollArea:onLayoutUpdate() + self:updateScrollBars() +end diff --git a/modules/core_lib/widgets/uiscrollbar.lua b/modules/core_lib/widgets/uiscrollbar.lua index a80e03be..a9470466 100644 --- a/modules/core_lib/widgets/uiscrollbar.lua +++ b/modules/core_lib/widgets/uiscrollbar.lua @@ -18,9 +18,12 @@ local function calcValues(self) end local range = self.maximum - self.minimum + 1 - local proportion = math.max(range*(self.step/range), 1)/range + local proportion = math.min(math.max(range*(self.step/range), 1), range)/range local px = math.max(math.floor(proportion * pxrange), 10) - local offset = ((self.value / (range - 1)) - 0.5) * (pxrange - px) + local offset = 0 + if range > 1 then + offset = (((self.value - self.minimum) / (range - 1)) - 0.5) * (pxrange - px) + end return range, pxrange, px, offset, center end @@ -54,7 +57,7 @@ local function parseSliderPos(self, pos) end local range, pxrange, px, offset, center = calcValues(self) offset = math.min(math.max(point - center, -pxrange/2), pxrange/2) - local newvalue = math.floor(((offset / (pxrange - px)) + 0.5) * (range - 1)) + local newvalue = math.floor(((offset / (pxrange - px)) + 0.5) * (range - 1)) + self.minimum self:setValue(newvalue) end diff --git a/modules/core_lib/widgets/uitabbar.lua b/modules/core_lib/widgets/uitabbar.lua index 4f1709ab..40909f76 100644 --- a/modules/core_lib/widgets/uitabbar.lua +++ b/modules/core_lib/widgets/uitabbar.lua @@ -32,6 +32,7 @@ function UITabBar:addTab(text, panel) end local tab = createWidget(self:getStyleName() .. 'Button', self) + panel.isTab = true tab.tabPanel = panel tab.tabBar = self tab:setText(text) @@ -68,8 +69,8 @@ end function UITabBar:selectTab(tab) if self.currentTab == tab then return end if self.contentWidget then - local selectedWidget = self.contentWidget:getFirstChild() - if selectedWidget then + local selectedWidget = self.contentWidget:getLastChild() + if selectedWidget and selectedWidget.isTab then self.contentWidget:removeChild(selectedWidget) end self.contentWidget:addChild(tab.tabPanel) diff --git a/modules/core_styles/styles/panels.otui b/modules/core_styles/styles/panels.otui index 883cea27..87b7fcd6 100644 --- a/modules/core_styles/styles/panels.otui +++ b/modules/core_styles/styles/panels.otui @@ -1,6 +1,9 @@ Panel < UIWidget phantom: true +ScrollablePanel < UIScrollArea + phantom: true + FlatPanel < Panel image-source: /core_styles/styles/images/panel_flat.png image-border: 1 diff --git a/modules/game/widgets/uigamemap.lua b/modules/game/widgets/uigamemap.lua index 9842b5eb..c81f8fc9 100644 --- a/modules/game/widgets/uigamemap.lua +++ b/modules/game/widgets/uigamemap.lua @@ -57,23 +57,19 @@ function UIGameMap:onDrop(widget, mousePos) return true end -function UIGameMap:onClick(mousePosition) - local tile = self:getTile(mousePosition) - if tile == nil then return false end - local dirs = g_map.findPath(g_game.getLocalPlayer():getPosition(), tile:getPosition(), 255) - if #dirs == 0 then - TextMessage.displayStatus('There is no way.') - return true - end - g_game.autoWalk(dirs) - return true -end - function UIGameMap:onMouseRelease(mousePosition, mouseButton) local tile = self:getTile(mousePosition) if tile == nil then return false end if GameInterface.processMouseAction(mousePosition, mouseButton, nil, tile:getTopLookThing(), tile:getTopUseThing(), tile:getTopCreature(), tile:getTopMultiUseThing()) then return true + elseif mouseButton == MouseLeftButton and self:isPressed() then + local dirs = g_map.findPath(g_game.getLocalPlayer():getPosition(), tile:getPosition(), 255) + if #dirs == 0 then + TextMessage.displayStatus('There is no way.') + return true + end + g_game.autoWalk(dirs) + return true end return false end diff --git a/modules/game_console/console.lua b/modules/game_console/console.lua index 3699a574..1e297efe 100644 --- a/modules/game_console/console.lua +++ b/modules/game_console/console.lua @@ -43,7 +43,7 @@ local SayModes = { } local consolePanel -local consoleBuffer +local consoleContentPanel local consoleTabBar local consoleLineEdit local channels @@ -51,6 +51,7 @@ local messageHistory = { } local currentMessageIndex = 0 local MaxHistory = 1000 local channelsWindow +local MAX_LINES = 100 -- private functions local function navigateMessageHistory(step) @@ -159,9 +160,9 @@ function Console.init() consolePanel = displayUI('console.otui', GameInterface.getBottomPanel()) consoleLineEdit = consolePanel:getChildById('consoleLineEdit') - consoleBuffer = consolePanel:getChildById('consoleBuffer') + consoleContentPanel = consolePanel:getChildById('consoleContentPanel') consoleTabBar = consolePanel:getChildById('consoleTabBar') - consoleTabBar:setContentWidget(consoleBuffer) + consoleTabBar:setContentWidget(consoleContentPanel) channels = {} Console.addChannel('Default', 0) @@ -208,7 +209,7 @@ function Console.terminate() consolePanel:destroy() consolePanel = nil consoleLineEdit = nil - consoleBuffer = nil + consoleContentPanel = nil consoleTabBar = nil Console = nil @@ -323,13 +324,15 @@ function Console.addTabText(text, speaktype, tab) end local panel = consoleTabBar:getTabPanel(tab) - local label = createWidget('ConsoleLabel', panel) + local consoleBuffer = panel:getChildById('consoleBuffer') + local label = createWidget('ConsoleLabel', consoleBuffer) + label:setId('consoleLabel' .. panel:getChildCount()) label:setText(text) label:setColor(speaktype.color) consoleTabBar:blinkTab(tab) - if panel:getChildCount() > 10 then - panel:removeChild(panel:getFirstChild()) + if consoleBuffer:getChildCount() > MAX_LINES then + consoleBuffer:getFirstChild():destroy() end end diff --git a/modules/game_console/console.otui b/modules/game_console/console.otui index dd9d095c..44db53ac 100644 --- a/modules/game_console/console.otui +++ b/modules/game_console/console.otui @@ -5,9 +5,30 @@ ConsoleLabel < UILabel ConsoleTabBar < TabBar ConsoleTabBarPanel < TabBarPanel - layout: - type: verticalBox - align-bottom: true + id: consoleTab + + ScrollablePanel + id: consoleBuffer + // height = max lines * 14 + anchors.fill: parent + margin-right: 12 + vertical-scrollbar: consoleScrollBar + layout: + type: verticalBox + align-bottom: true + border-width: 1 + border-color: #101317 + background: #00000044 + inverted-scroll: true + padding: 1 + + VerticalScrollBar + id: consoleScrollBar + anchors.top: parent.top + anchors.bottom: parent.bottom + anchors.right: parent.right + step: 14 + ConsoleTabBarButton < TabBarButton Panel @@ -60,15 +81,16 @@ Panel @onClick: g_game.requestChannels() Panel - id: consoleBuffer + id: consoleContentPanel anchors.top: prev.bottom anchors.left: parent.left anchors.right: parent.right anchors.bottom: consoleLineEdit.top - margin-right: 6 margin-left: 6 + margin-right: 6 margin-bottom: 4 margin-top: 4 + padding: 1 focusable: false TabButton diff --git a/src/framework/luafunctions.cpp b/src/framework/luafunctions.cpp index 611bf41c..6ac2f434 100644 --- a/src/framework/luafunctions.cpp +++ b/src/framework/luafunctions.cpp @@ -112,6 +112,7 @@ void Application::registerLuaFunctions() g_lua.bindClassMemberFunction("isChildLocked", &UIWidget::isChildLocked); g_lua.bindClassMemberFunction("hasChild", &UIWidget::hasChild); g_lua.bindClassMemberFunction("getChildIndex", &UIWidget::getChildIndex); + g_lua.bindClassMemberFunction("getClippingRect", &UIWidget::getClippingRect); g_lua.bindClassMemberFunction("getChildrenRect", &UIWidget::getChildrenRect); g_lua.bindClassMemberFunction("getAnchoredLayout", &UIWidget::getAnchoredLayout); g_lua.bindClassMemberFunction("getRootParent", &UIWidget::getRootParent); diff --git a/src/framework/ui/uianchorlayout.cpp b/src/framework/ui/uianchorlayout.cpp index 16ac1164..d28011af 100644 --- a/src/framework/ui/uianchorlayout.cpp +++ b/src/framework/ui/uianchorlayout.cpp @@ -127,7 +127,7 @@ void UIAnchorLayout::updateWidget(const UIWidgetPtr& widget, UIAnchorGroup& anch // determine hooked widget edge point Rect hookedWidgetRect = hookedWidget->getRect(); if(hookedWidget == parentWidget) - hookedWidgetRect = parentWidget->getChildrenRect(); + hookedWidgetRect = parentWidget->getClippingRect(); int point = 0; switch(anchor.getHookedEdge()) { diff --git a/src/framework/ui/uigridlayout.cpp b/src/framework/ui/uigridlayout.cpp index c5779927..57f91be7 100644 --- a/src/framework/ui/uigridlayout.cpp +++ b/src/framework/ui/uigridlayout.cpp @@ -67,8 +67,8 @@ void UIGridLayout::internalUpdate() UIWidgetPtr parentWidget = getParentWidget(); UIWidgetList widgets = parentWidget->getChildren(); - Rect childrenRect = parentWidget->getChildrenRect(); - Point topLeft = childrenRect.topLeft(); + Rect clippingRect = parentWidget->getClippingRect(); + Point topLeft = clippingRect.topLeft(); int index = 0; for(const UIWidgetPtr& widget : widgets) { diff --git a/src/framework/ui/uihorizontallayout.cpp b/src/framework/ui/uihorizontallayout.cpp index 1b002d24..b1780a1a 100644 --- a/src/framework/ui/uihorizontallayout.cpp +++ b/src/framework/ui/uihorizontallayout.cpp @@ -45,8 +45,8 @@ void UIHorizontalLayout::internalUpdate() if(m_alignRight) std::reverse(widgets.begin(), widgets.end()); - Rect childrenRect = parentWidget->getChildrenRect(); - Point pos = (m_alignRight) ? childrenRect.topRight() : childrenRect.topLeft(); + Rect clippingRect = parentWidget->getClippingRect(); + Point pos = (m_alignRight) ? clippingRect.topRight() : clippingRect.topLeft(); int prefferedWidth = 0; int gap; @@ -62,15 +62,15 @@ void UIHorizontalLayout::internalUpdate() if(widget->isFixedSize()) { // center it - pos.y = childrenRect.top() + (childrenRect.height() - (widget->getMarginTop() + widget->getHeight() + widget->getMarginBottom()))/2; + pos.y = clippingRect.top() + (clippingRect.height() - (widget->getMarginTop() + widget->getHeight() + widget->getMarginBottom()))/2; pos.y = std::max(pos.y, parentWidget->getY()); } else { // expand height - size.setHeight(childrenRect.height() - (widget->getMarginTop() + widget->getMarginBottom())); - pos.y = childrenRect.top() + (childrenRect.height() - size.height())/2; + size.setHeight(clippingRect.height() - (widget->getMarginTop() + widget->getMarginBottom())); + pos.y = clippingRect.top() + (clippingRect.height() - size.height())/2; } - widget->setRect(Rect(pos, size)); + widget->setRect(Rect(pos + parentWidget->getVirtualOffset(), size)); gap = (m_alignRight) ? -widget->getMarginLeft() : (widget->getWidth() + widget->getMarginRight()); gap += m_spacing; diff --git a/src/framework/ui/uilayout.cpp b/src/framework/ui/uilayout.cpp index 1adf530a..c55b2dc0 100644 --- a/src/framework/ui/uilayout.cpp +++ b/src/framework/ui/uilayout.cpp @@ -21,6 +21,7 @@ */ #include "uilayout.h" +#include "uiwidget.h" #include @@ -29,9 +30,15 @@ void UILayout::update() if(m_updateDisabled) return; - assert(!m_updating); + if(m_updating) { + updateLater(); + return; + } + m_updating = true; internalUpdate(); + if(UIWidgetPtr parentWidget = getParentWidget()) + parentWidget->onLayoutUpdate(); m_updating = false; } diff --git a/src/framework/ui/uimanager.cpp b/src/framework/ui/uimanager.cpp index 0fd802a0..73352c65 100644 --- a/src/framework/ui/uimanager.cpp +++ b/src/framework/ui/uimanager.cpp @@ -87,22 +87,16 @@ void UIManager::inputEvent(const InputEvent& event) } break; case Fw::MouseReleaseInputEvent: { - bool accepted = false; + m_mouseReceiver->propagateOnMouseRelease(event.mousePos, event.mouseButton); if(event.mouseButton == Fw::MouseLeftButton) { // release pressed widget - if(m_pressedWidget) { - if(updatePressedWidget(nullptr, event.mousePos)) - accepted = true; - } + if(m_pressedWidget) + updatePressedWidget(nullptr, event.mousePos); // release dragging widget - if(m_draggingWidget) { - if(updateDraggingWidget(nullptr, event.mousePos)) - accepted = true; - } + if(m_draggingWidget) + updateDraggingWidget(nullptr, event.mousePos); } - if(!accepted) - m_mouseReceiver->propagateOnMouseRelease(event.mousePos, event.mouseButton); break; } case Fw::MouseMoveInputEvent: { @@ -131,14 +125,7 @@ bool UIManager::updatePressedWidget(const UIWidgetPtr& newPressedWidget, const P bool accepted = false; UIWidgetPtr oldPressedWidget = m_pressedWidget; - m_pressedWidget = newPressedWidget; - - if(newPressedWidget) - newPressedWidget->updateState(Fw::PressedState); - if(oldPressedWidget) { - oldPressedWidget->updateState(Fw::PressedState); - if(oldPressedWidget->isEnabled()) { // when releasing mouse inside pressed widget area send onClick event if(!clickedPos.isNull() && oldPressedWidget->containsPoint(clickedPos)) { @@ -150,6 +137,14 @@ bool UIManager::updatePressedWidget(const UIWidgetPtr& newPressedWidget, const P } } + m_pressedWidget = newPressedWidget; + + if(newPressedWidget) + newPressedWidget->updateState(Fw::PressedState); + + if(oldPressedWidget) + oldPressedWidget->updateState(Fw::PressedState); + return accepted; } @@ -363,7 +358,12 @@ UIWidgetPtr UIManager::loadUI(const std::string& file, const UIWidgetPtr& parent UIWidgetPtr UIManager::createWidgetFromStyle(const std::string& styleName, const UIWidgetPtr& parent) { OTMLNodePtr node = OTMLNode::create(styleName); - return createWidgetFromOTML(node, parent); + try { + return createWidgetFromOTML(node, parent); + } catch(Exception& e) { + logError("failed to create widget from style '", styleName, "': ", e.what()); + return nullptr; + } } UIWidgetPtr UIManager::createWidgetFromOTML(const OTMLNodePtr& widgetNode, const UIWidgetPtr& parent) diff --git a/src/framework/ui/uiverticallayout.cpp b/src/framework/ui/uiverticallayout.cpp index af2affad..d34ef789 100644 --- a/src/framework/ui/uiverticallayout.cpp +++ b/src/framework/ui/uiverticallayout.cpp @@ -45,8 +45,8 @@ void UIVerticalLayout::internalUpdate() if(m_alignBottom) std::reverse(widgets.begin(), widgets.end()); - Rect childrenRect = parentWidget->getChildrenRect(); - Point pos = (m_alignBottom) ? childrenRect.bottomLeft() : childrenRect.topLeft(); + Rect clippingRect = parentWidget->getClippingRect(); + Point pos = (m_alignBottom) ? clippingRect.bottomLeft() : clippingRect.topLeft(); int prefferedHeight = 0; int gap; @@ -62,15 +62,15 @@ void UIVerticalLayout::internalUpdate() if(widget->isFixedSize()) { // center it - pos.x = childrenRect.left() + (childrenRect.width() - (widget->getMarginLeft() + widget->getWidth() + widget->getMarginRight()))/2; + pos.x = clippingRect.left() + (clippingRect.width() - (widget->getMarginLeft() + widget->getWidth() + widget->getMarginRight()))/2; pos.x = std::max(pos.x, parentWidget->getX()); } else { // expand width - size.setWidth(childrenRect.width() - (widget->getMarginLeft() + widget->getMarginRight())); - pos.x = childrenRect.left() + (childrenRect.width() - size.width())/2; + size.setWidth(clippingRect.width() - (widget->getMarginLeft() + widget->getMarginRight())); + pos.x = clippingRect.left() + (clippingRect.width() - size.width())/2; } - widget->setRect(Rect(pos, size)); + widget->setRect(Rect(pos + parentWidget->getVirtualOffset(), size)); gap = (m_alignBottom) ? -widget->getMarginTop() : (widget->getHeight() + widget->getMarginBottom()); gap += m_spacing; diff --git a/src/framework/ui/uiwidget.cpp b/src/framework/ui/uiwidget.cpp index ea2cb376..2ecaa749 100644 --- a/src/framework/ui/uiwidget.cpp +++ b/src/framework/ui/uiwidget.cpp @@ -57,7 +57,13 @@ void UIWidget::draw(const Rect& visibleRect) g_graphics.beginClipping(visibleRect); drawSelf(); - drawChildren(visibleRect); + + if(m_children.size() > 0) { + if(m_clipping) + g_graphics.beginClipping(visibleRect.intersection(getClippingRect())); + + drawChildren(visibleRect); + } if(m_clipping) g_graphics.endClipping(); @@ -890,13 +896,29 @@ int UIWidget::getChildIndex(const UIWidgetPtr& child) return -1; } -Rect UIWidget::getChildrenRect() +Rect UIWidget::getClippingRect() { Rect rect = m_rect; rect.expand(-m_padding.top, -m_padding.right, -m_padding.bottom, -m_padding.left); return rect; } +Rect UIWidget::getChildrenRect() +{ + Rect childrenRect; + for(const UIWidgetPtr& child : m_children) { + if(!child->isExplicitlyVisible() || !child->getRect().isValid() || child->getOpacity() == 0.0f) + continue; + if(!childrenRect.isValid()) + childrenRect = child->getRect(); + else + childrenRect = childrenRect.united(child->getRect()); + } + if(!childrenRect.isValid()) + childrenRect = getClippingRect(); + return childrenRect; +} + UIAnchorLayoutPtr UIWidget::getAnchoredLayout() { UIWidgetPtr parent = getParent(); @@ -1231,6 +1253,13 @@ void UIWidget::onGeometryChange(const Rect& oldRect, const Rect& newRect) callLuaField("onGeometryChange", oldRect, newRect); } +void UIWidget::onLayoutUpdate() +{ + callLuaField("onLayoutUpdate"); + if(UIWidgetPtr parent = getParent()) + parent->onLayoutUpdate(); +} + void UIWidget::onFocusChange(bool focused, Fw::FocusReason reason) { callLuaField("onFocusChange", focused, reason); diff --git a/src/framework/ui/uiwidget.h b/src/framework/ui/uiwidget.h index aa2b231f..15f89eac 100644 --- a/src/framework/ui/uiwidget.h +++ b/src/framework/ui/uiwidget.h @@ -132,6 +132,7 @@ public: bool isChildLocked(const UIWidgetPtr& child); bool hasChild(const UIWidgetPtr& child); int getChildIndex(const UIWidgetPtr& child); + Rect getClippingRect(); Rect getChildrenRect(); UIAnchorLayoutPtr getAnchoredLayout(); UIWidgetPtr getRootParent(); @@ -173,6 +174,7 @@ private: protected: virtual void onStyleApply(const std::string& styleName, const OTMLNodePtr& styleNode); virtual void onGeometryChange(const Rect& oldRect, const Rect& newRect); + virtual void onLayoutUpdate(); virtual void onFocusChange(bool focused, Fw::FocusReason reason); virtual void onChildFocusChange(const UIWidgetPtr& focusedChild, const UIWidgetPtr& unfocusedChild, Fw::FocusReason reason); virtual void onHoverChange(bool hovered); @@ -191,6 +193,8 @@ protected: virtual bool onClick(const Point& mousePos); virtual bool onDoubleClick(const Point& mousePos); + friend class UILayout; + bool propagateOnKeyText(const std::string& keyText); bool propagateOnKeyDown(uchar keyCode, int keyboardModifiers); bool propagateOnKeyPress(uchar keyCode, int keyboardModifiers, int autoRepeatTicks); diff --git a/src/otclient/ui/uicreature.cpp b/src/otclient/ui/uicreature.cpp index b9929747..f2e43489 100644 --- a/src/otclient/ui/uicreature.cpp +++ b/src/otclient/ui/uicreature.cpp @@ -30,7 +30,7 @@ void UICreature::drawSelf() if(m_creature) { g_painter.setColor(Color::white); - Rect drawRect = getChildrenRect(); + Rect drawRect = getClippingRect(); m_creature->drawOutfit(drawRect, !m_fixedCreatureSize); } } diff --git a/src/otclient/ui/uimap.cpp b/src/otclient/ui/uimap.cpp index 12974d0a..5df67881 100644 --- a/src/otclient/ui/uimap.cpp +++ b/src/otclient/ui/uimap.cpp @@ -64,7 +64,7 @@ void UIMap::zoomIn() m_mapView->setVisibleDimension(Size(dimensionWidth, dimensionHeight)); - Rect mapRect = getChildrenRect().expanded(-1); + Rect mapRect = getClippingRect().expanded(-1); Size mapSize = m_mapView->getVisibleSize(); mapSize.scale(mapRect.size(), Fw::KeepAspectRatio); @@ -85,7 +85,7 @@ void UIMap::zoomOut() m_mapView->setVisibleDimension(Size(dimensionWidth, dimensionHeight)); - Rect mapRect = getChildrenRect().expanded(-1); + Rect mapRect = getClippingRect().expanded(-1); Size mapSize = m_mapView->getVisibleSize(); mapSize.scale(mapRect.size(), Fw::KeepAspectRatio); @@ -116,7 +116,7 @@ void UIMap::onGeometryChange(const Rect& oldRect, const Rect& newRect) { UIWidget::onGeometryChange(oldRect, newRect); - Rect mapRect = getChildrenRect().expanded(-1); + Rect mapRect = getClippingRect().expanded(-1); Size mapSize = m_mapView->getVisibleSize(); mapSize.scale(mapRect.size(), Fw::KeepAspectRatio);