[Libreoffice-commits] core.git: include/vcl vcl/qt5 vcl/source

Jan-Marek Glogowski (via logerrit) logerrit at kemper.freedesktop.org
Fri Jun 21 14:50:30 UTC 2019


 include/vcl/salnativewidgets.hxx     |   26 +++++++++++
 vcl/qt5/Qt5Graphics_Controls.cxx     |   81 ++++++++++++++++++++++++++++++++++-
 vcl/source/control/tabctrl.cxx       |   31 +++++++++----
 vcl/source/outdev/nativecontrols.cxx |   19 +++++++-
 4 files changed, 145 insertions(+), 12 deletions(-)

New commits:
commit 430b406e5e8249983fc030aa0f867372f5da74d2
Author:     Jan-Marek Glogowski <glogow at fbihome.de>
AuthorDate: Thu Jun 20 13:00:58 2019 +0000
Commit:     Jan-Marek Glogowski <glogow at fbihome.de>
CommitDate: Fri Jun 21 16:48:37 2019 +0200

    tdf#105884 Qt5 implement TabControl theming
    
    Drawing a QTabWidget is a really complex procedure. The main
    problems I had were the adjustment of the frame, which I totally
    missed in the Qt code for a long time.
    
    Then there is the frame gap, which Qt draws by simply overlapping
    the items a bit with the frame. And all the calculations need the
    tabs together with the pane. None of it really fits very good into
    the way VCL handles drawing the TabControl and since I needed a
    way back from the plugin into VCL for the nOverlap value, there is
    this hack using a static. I hope nOverlap never changes.
    
    Change-Id: I8fe6eb12d39a2ac7f6fb89424586cac76e12545b
    Reviewed-on: https://gerrit.libreoffice.org/74480
    Tested-by: Jenkins
    Reviewed-by: Jan-Marek Glogowski <glogow at fbihome.de>

diff --git a/include/vcl/salnativewidgets.hxx b/include/vcl/salnativewidgets.hxx
index 03d9a0eee00e..2a67ceb47b24 100644
--- a/include/vcl/salnativewidgets.hxx
+++ b/include/vcl/salnativewidgets.hxx
@@ -181,6 +181,9 @@ enum class ControlPart
 // hardcoded 2 pixel overlap between adjacent tabs
     TabsDrawRtl             = 3000,
 
+// Qt doesn't have a separate header to draw
+    TabPaneWithHeader       = 3001,
+
 // For themes that do not want to have the focus
 // rectangle part drawn by VCL but take care of the
 // whole inner control part by themselves
@@ -340,6 +343,29 @@ class VCL_DLLPUBLIC SliderValue : public ImplControlValue
         SliderValue & operator =(SliderValue &&) = delete; // due to ImplControlValue
 };
 
+class VCL_DLLPUBLIC TabPaneValue : public ImplControlValue
+{
+public:
+    tools::Rectangle m_aTabHeaderRect;
+    tools::Rectangle m_aSelectedTabRect;
+    // increased tab size, so it'll overlab the frame rect when draing
+    // static value, as there is currently no sane way to return additional data
+    static int m_nOverlap;
+
+    TabPaneValue(const tools::Rectangle &rTabHeaderRect, const tools::Rectangle &rSelectedTabRect)
+        : ImplControlValue(ControlType::TabPane, 0)
+        , m_aTabHeaderRect(rTabHeaderRect)
+        , m_aSelectedTabRect(rSelectedTabRect)
+    {
+    }
+    TabPaneValue* clone() const override;
+
+    TabPaneValue(TabPaneValue const &) = default;
+    TabPaneValue(TabPaneValue &&) = default;
+    TabPaneValue & operator =(TabPaneValue const &) = delete;
+    TabPaneValue & operator =(TabPaneValue &&) = delete;
+};
+
 /* TabitemValue:
  *
  *   Value container for tabitems.
diff --git a/vcl/qt5/Qt5Graphics_Controls.cxx b/vcl/qt5/Qt5Graphics_Controls.cxx
index 6cefef623a73..73a47e5e5ffa 100644
--- a/vcl/qt5/Qt5Graphics_Controls.cxx
+++ b/vcl/qt5/Qt5Graphics_Controls.cxx
@@ -105,6 +105,10 @@ bool Qt5Graphics_Controls::isNativeControlSupported(ControlType type, ControlPar
         case ControlType::Slider:
             return (part == ControlPart::TrackHorzArea || part == ControlPart::TrackVertArea);
 
+        case ControlType::TabItem:
+        case ControlType::TabPane:
+            return ((part == ControlPart::Entire) || part == ControlPart::TabPaneWithHeader);
+
         default:
             break;
     }
@@ -112,7 +116,6 @@ bool Qt5Graphics_Controls::isNativeControlSupported(ControlType type, ControlPar
     return false;
 }
 
-/// helper drawing methods
 namespace
 {
 void draw(QStyle::ControlElement element, QStyleOption* option, QImage* image,
@@ -163,6 +166,27 @@ void lcl_drawFrame(QStyle::PrimitiveElement element, QImage* image, QStyle::Stat
         painter.setClipRegion(QRegion(aRect).subtracted(aRect.adjusted(fw, fw, -fw, -fw)));
     QApplication::style()->drawPrimitive(element, &option, &painter);
 }
+
+void lcl_fillQStyleOptionTab(const ImplControlValue& value, QStyleOptionTab& sot)
+{
+    const TabitemValue& rValue = static_cast<const TabitemValue&>(value);
+    if (rValue.isFirst())
+        sot.position = rValue.isLast() ? QStyleOptionTab::OnlyOneTab : QStyleOptionTab::Beginning;
+    else if (rValue.isLast())
+        sot.position = rValue.isFirst() ? QStyleOptionTab::OnlyOneTab : QStyleOptionTab::End;
+    else
+        sot.position = QStyleOptionTab::Middle;
+}
+
+void lcl_fullQStyleOptionTabWidgetFrame(QStyleOptionTabWidgetFrame& option)
+{
+    option.state = QStyle::State_Enabled;
+    option.rightCornerWidgetSize = QSize(0, 0);
+    option.leftCornerWidgetSize = QSize(0, 0);
+    option.lineWidth = QApplication::style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
+    option.midLineWidth = 0;
+    option.shape = QTabBar::RoundedNorth;
+}
 }
 
 bool Qt5Graphics_Controls::drawNativeControl(ControlType type, ControlPart part,
@@ -609,6 +633,34 @@ bool Qt5Graphics_Controls::drawNativeControl(ControlType type, ControlPart part,
         draw(QStyle::CE_ProgressBar, &option, m_image.get(),
              vclStateValue2StateFlag(nControlState, value));
     }
+    else if (type == ControlType::TabItem && part == ControlPart::Entire)
+    {
+        QStyleOptionTab sot;
+        lcl_fillQStyleOptionTab(value, sot);
+        draw(QStyle::CE_TabBarTabShape, &sot, m_image.get(),
+             vclStateValue2StateFlag(nControlState, value));
+    }
+    else if (type == ControlType::TabPane && part == ControlPart::Entire)
+    {
+        const TabPaneValue& rValue = static_cast<const TabPaneValue&>(value);
+
+        // get the overlap size for the tabs, so they will overlap the frame
+        QStyleOptionTab tabOverlap;
+        tabOverlap.shape = QTabBar::RoundedNorth;
+        TabPaneValue::m_nOverlap
+            = QApplication::style()->pixelMetric(QStyle::PM_TabBarBaseOverlap, &tabOverlap);
+
+        QStyleOptionTabWidgetFrame option;
+        lcl_fullQStyleOptionTabWidgetFrame(option);
+        option.tabBarRect = toQRect(rValue.m_aTabHeaderRect);
+        option.selectedTabRect
+            = rValue.m_aSelectedTabRect.IsEmpty() ? QRect() : toQRect(rValue.m_aSelectedTabRect);
+        option.tabBarSize = toQSize(rValue.m_aTabHeaderRect.GetSize());
+        option.rect = m_image->rect();
+        QRect aRect = QApplication::style()->subElementRect(QStyle::SE_TabWidgetTabPane, &option);
+        draw(QStyle::PE_FrameTabWidget, &option, m_image.get(),
+             vclStateValue2StateFlag(nControlState, value), aRect);
+    }
     else
     {
         returnVal = false;
@@ -818,7 +870,7 @@ bool Qt5Graphics_Controls::getNativeControlRegion(ControlType type, ControlPart
                 auto nStyle = static_cast<DrawFrameFlags>(val.getNumericVal() & 0xFFF0);
                 if (nStyle & DrawFrameFlags::NoDraw)
                 {
-                    int nFrameWidth
+                    const int nFrameWidth
                         = QApplication::style()->pixelMetric(QStyle::PM_DefaultFrameWidth);
                     contentRect.adjust(nFrameWidth, nFrameWidth, -nFrameWidth, -nFrameWidth);
                 }
@@ -912,6 +964,31 @@ bool Qt5Graphics_Controls::getNativeControlRegion(ControlType type, ControlPart
             }
             break;
         }
+        case ControlType::TabItem:
+        {
+            QStyleOptionTab sot;
+            lcl_fillQStyleOptionTab(val, sot);
+            QSize aMinSize = QApplication::style()->sizeFromContents(QStyle::CT_TabBarTab, &sot,
+                                                                     contentRect.size());
+            contentRect.setSize(aMinSize);
+            boundingRect = contentRect;
+            retVal = true;
+            break;
+        }
+        case ControlType::TabPane:
+        {
+            const TabPaneValue& rValue = static_cast<const TabPaneValue&>(val);
+            QStyleOptionTabWidgetFrame sotwf;
+            lcl_fullQStyleOptionTabWidgetFrame(sotwf);
+            QSize aMinSize = QApplication::style()->sizeFromContents(
+                QStyle::CT_TabWidget, &sotwf,
+                QSize(std::max(rValue.m_aTabHeaderRect.GetWidth(), controlRegion.GetWidth()),
+                      rValue.m_aTabHeaderRect.GetHeight() + controlRegion.GetHeight()));
+            contentRect.setSize(aMinSize);
+            boundingRect = contentRect;
+            retVal = true;
+            break;
+        }
         default:
             break;
     }
diff --git a/vcl/source/control/tabctrl.cxx b/vcl/source/control/tabctrl.cxx
index d23db1eaf9cf..9b269e8ef66c 100644
--- a/vcl/source/control/tabctrl.cxx
+++ b/vcl/source/control/tabctrl.cxx
@@ -865,6 +865,7 @@ void TabControl::ImplDrawItem(vcl::RenderContext& rRenderContext, ImplTabItem co
             tiValue.mnAlignment |= TabitemFlags::LastInGroup;
 
         tools::Rectangle aCtrlRegion( pItem->maRect );
+        aCtrlRegion.AdjustBottom(TabPaneValue::m_nOverlap);
         bNativeOK = rRenderContext.DrawNativeControl(ControlType::TabItem, ControlPart::Entire,
                                                      aCtrlRegion, nState, tiValue, OUString() );
     }
@@ -1098,7 +1099,22 @@ void TabControl::Paint( vcl::RenderContext& rRenderContext, const tools::Rectang
 
     if (rRenderContext.IsNativeControlSupported(ControlType::TabPane, ControlPart::Entire))
     {
-        const ImplControlValue aControlValue;
+        const bool bPaneWithHeader = rRenderContext.IsNativeControlSupported(ControlType::TabPane, ControlPart::TabPaneWithHeader);
+        tools::Rectangle aHeaderRect(aRect.Left(), 0, aRect.Right(), aRect.Top());
+        if (bPaneWithHeader)
+        {
+            aRect.SetTop(0);
+            if (mpTabCtrlData->maItemList.size())
+            {
+                long nRight = 0;
+                for (auto &item : mpTabCtrlData->maItemList)
+                    if (item.m_bVisible)
+                        nRight = item.maRect.Right();
+                assert(nRight);
+                aHeaderRect.SetRight(nRight);
+            }
+        }
+        const TabPaneValue aTabPaneValue(aHeaderRect, pCurItem ? pCurItem->maRect : tools::Rectangle());
 
         ControlState nState = ControlState::ENABLED;
         if (!IsEnabled())
@@ -1108,15 +1124,12 @@ void TabControl::Paint( vcl::RenderContext& rRenderContext, const tools::Rectang
 
         if (lcl_canPaint(rRenderContext, rRect, aRect))
             rRenderContext.DrawNativeControl(ControlType::TabPane, ControlPart::Entire,
-                                             aRect, nState, aControlValue, OUString());
+                                             aRect, nState, aTabPaneValue, OUString());
 
-        if (rRenderContext.IsNativeControlSupported(ControlType::TabHeader, ControlPart::Entire))
-        {
-            tools::Rectangle aHeaderRect(aRect.Left(), 0, aRect.Right(), aRect.Top());
-            if (lcl_canPaint(rRenderContext, rRect, aHeaderRect))
-                rRenderContext.DrawNativeControl(ControlType::TabHeader, ControlPart::Entire,
-                                                 aHeaderRect, nState, aControlValue, OUString());
-        }
+        if (!bPaneWithHeader && rRenderContext.IsNativeControlSupported(ControlType::TabHeader, ControlPart::Entire)
+                && lcl_canPaint(rRenderContext, rRect, aHeaderRect))
+            rRenderContext.DrawNativeControl(ControlType::TabHeader, ControlPart::Entire,
+                                             aHeaderRect, nState, aTabPaneValue, OUString());
     }
     else
     {
diff --git a/vcl/source/outdev/nativecontrols.cxx b/vcl/source/outdev/nativecontrols.cxx
index ef2295808a6d..00218344ff7d 100644
--- a/vcl/source/outdev/nativecontrols.cxx
+++ b/vcl/source/outdev/nativecontrols.cxx
@@ -95,6 +95,14 @@ SliderValue* SliderValue::clone() const
     return new SliderValue( *this );
 }
 
+int TabPaneValue::m_nOverlap = 0;
+
+TabPaneValue* TabPaneValue::clone() const
+{
+    assert(typeid(const TabPaneValue) == typeid(*this));
+    return new TabPaneValue(*this);
+}
+
 TabitemValue::~TabitemValue()
 {
 }
@@ -229,6 +237,15 @@ static std::shared_ptr< ImplControlValue > TransformControlValue( const ImplCont
             pNew->maGripRect = rDev.ImplLogicToDevicePixel( pTVal->maGripRect );
         }
         break;
+    case ControlType::TabPane:
+        {
+            const TabPaneValue* pTIVal = static_cast<const TabPaneValue*>(&rVal);
+            TabPaneValue* pNew = new TabPaneValue(*pTIVal);
+            pNew->m_aTabHeaderRect = rDev.ImplLogicToDevicePixel(pTIVal->m_aTabHeaderRect);
+            pNew->m_aSelectedTabRect = rDev.ImplLogicToDevicePixel(pTIVal->m_aSelectedTabRect);
+            aResult.reset(pNew);
+        }
+        break;
     case ControlType::TabItem:
         {
             const TabitemValue* pTIVal = static_cast<const TabitemValue*>(&rVal);
@@ -263,7 +280,7 @@ static std::shared_ptr< ImplControlValue > TransformControlValue( const ImplCont
         }
         break;
     default:
-        OSL_FAIL( "unknown ImplControlValue type !" );
+        std::abort();
         break;
     }
     return aResult;


More information about the Libreoffice-commits mailing list