// Property of Kitware, Inc. and KbPort.
// PROPRIETARY: DO NOT DISTRIBUTE.

#include "WidgetStyle.h"

#include "Icon.h"

#include <qtColorUtil.h>
#include <qtIndexRange.h>

#include <QComboBox>
#include <QFormLayout>
#include <QFrame>
#include <QLabel>
#include <QLineEdit>
#include <QStyleOptionButton>
#include <QPainter>
#include <QPushButton>
#include <QToolButton>

namespace // anonymous
{
  constexpr auto RADIUS = 4;
  constexpr auto FRAME_WIDTH = 1;
  constexpr auto BUTTON_MARGIN = 6;
  constexpr auto BUTTON_ICON_SIZE = 64;
  constexpr auto SIDEBAR_BUTTON_MARGIN = 10;
  constexpr auto SIDEBAR_BUTTON_ICON_SIZE = 24;
  constexpr auto LINEEDIT_MARGIN = 4;
  constexpr auto CHECKBOX_SIZE = 16;
  constexpr auto CHECKBOX_MARGIN = 8;
  constexpr auto CHECK_OFFSET = 2;
  constexpr auto FORMLAYOUT_HSPACING = 20;
  constexpr auto FORMLAYOUT_HMARGIN = 10;

  // --------------------------------------------------------------------------
  template <typename T>
  T const* cast(QStyleOption const* opt)
  {
    return qstyleoption_cast<T const*>(opt);
  }

  // --------------------------------------------------------------------------
  QFont iconFont(QWidget const* w, int pixelSize = 0)
  {
    static auto const name = QStringLiteral("Pulse Icons");

    auto font = QFont{name};
    if (pixelSize)
      font.setPixelSize(pixelSize);
    else if (w && w->font().pointSizeF() > 0.0)
      font.setPointSizeF(w->font().pointSizeF());

    return font;
  }

  // --------------------------------------------------------------------------
  QPalette::ColorRole background(
    QWidget const* w, QPalette::ColorRole defaultRole)
  {
    return (w ? w->backgroundRole() : defaultRole);
  }

  // --------------------------------------------------------------------------
  QPalette::ColorRole foreground(
    QWidget const* w, QPalette::ColorRole defaultRole)
  {
    return (w ? w->foregroundRole() : defaultRole);
  }

  // --------------------------------------------------------------------------
  QColor paletteColor(QStyleOption const* opt, QPalette::ColorRole role)
  {
    if (!(opt->state & QStyle::State_Enabled))
      return opt->palette.color(QPalette::Disabled, role);
    if (!(opt->state & QStyle::State_Active))
      return opt->palette.color(QPalette::Inactive, role);
    return opt->palette.color(QPalette::Active, role);
  }

  // --------------------------------------------------------------------------
  QColor buttonTextColor(
    QStyleOption const* opt, QPalette::ColorRole role = QPalette::ButtonText)
  {
    auto const enabled = !!(opt->state & QStyle::State_Enabled);
    auto const hover = !!(opt->state & QStyle::State_MouseOver);
    auto const focus = !!(opt->state & QStyle::State_HasFocus);
    auto const pressed = !!(opt->state & QStyle::State_Sunken);

    if (!enabled)
      return opt->palette.color(QPalette::Disabled, role);
    if (pressed || hover || focus)
      return opt->palette.color(QPalette::Active, role);
    return opt->palette.color(QPalette::Inactive, role);
  }

  // --------------------------------------------------------------------------
  QPixmap pixmapBuffer(QPainter const* p, QSize const& size)
  {
    auto const dpr = (p->device() ? p->device()->devicePixelRatioF() : 1.0);

    if (dpr == 1.0)
      return QPixmap{size};

    auto const adjustedSize = (QSizeF{size} * dpr).toSize() + QSize{1, 1};

    QPixmap buffer{adjustedSize};
    buffer.setDevicePixelRatio(dpr);
    return buffer;
  }

  // --------------------------------------------------------------------------
  void setFontWeight(QWidget* widget, int weight)
  {
    auto font = widget->font();
    font.setWeight(weight);
    widget->setFont(font);
  }

  // --------------------------------------------------------------------------
  void setFormLayoutMetrics(QLayout* layout)
  {
    if (!layout)
      return;

    auto* const fl = qobject_cast<QFormLayout*>(layout);
    if (fl)
    {
      auto margins = fl->contentsMargins();
      margins.setLeft(FORMLAYOUT_HMARGIN);
      margins.setRight(FORMLAYOUT_HMARGIN);
      fl->setContentsMargins(margins);
      fl->setHorizontalSpacing(FORMLAYOUT_HSPACING);
    }

    for (auto const i : qtIndexRange(layout->count()))
    {
      auto* const item = layout->itemAt(i);
      if (item)
      {
        if (fl)
        {
          auto role = QFormLayout::FieldRole;
          fl->getItemPosition(i, nullptr, &role);

          if (role == QFormLayout::LabelRole)
          {
            if (auto* const label = qobject_cast<QLabel*>(item->widget()))
              setFontWeight(label, 70);
          }
        }

        setFormLayoutMetrics(item->layout());
      }
    }
  }

  // --------------------------------------------------------------------------
  void drawGlyph(QPainter* p, QWidget const* w, QRect const& rect,
                 QColor const& color, QString const& glyph)
  {
    p->setFont(iconFont(w));
    p->setPen(color);

    constexpr auto flags = Qt::AlignCenter | Qt::AlignVCenter;
    p->drawText(rect, flags, glyph);
  }

  // --------------------------------------------------------------------------
  void drawFrame(
    QStyleOptionFrame const* opt, QPainter* p, QWidget const* w)
  {
    auto const& r = QRectF{opt->rect};
    auto cr = QPalette::Mid;

    if (auto* const f = qobject_cast<QFrame const*>(w))
    {
      if (f->frameShadow() == QFrame::Plain)
        cr = QPalette::WindowText;
    }

    auto pen = QPen{paletteColor(opt, cr), 1.0};
    pen.setJoinStyle(Qt::MiterJoin);
    p->setPen(pen);

    switch (opt->frameShape)
    {
      case QFrame::HLine:
      {
        auto const y = r.center().y();
        p->drawLine(QPointF{r.left(), y}, QPointF{r.right() + 0.5, y});
        break;
      }

      case QFrame::VLine:
      {
        auto const x = r.center().x();
        p->drawLine(QPointF{x, r.top()}, QPointF{x, r.bottom() + 0.5});
        break;
      }

      case QFrame::Panel:
        p->drawRect(r.adjusted(+0.5, +0.5, -0.5, -0.5));
        break;

      default:
        // No frame
        break;
    }
  }

  // --------------------------------------------------------------------------
  void drawButtonFrame(
    QStyleOptionButton const* opt, QPainter* p)
  {
    auto const enabled = !!(opt->state & QStyle::State_Enabled);
    auto const hover = !!(opt->state & QStyle::State_MouseOver);
    auto const focus = !!(opt->state & QStyle::State_HasFocus);
    auto const pressed = !!(opt->state & QStyle::State_Sunken);

    auto const fillColor = [&]{
      if (!enabled)
        return opt->palette.color(QPalette::Disabled, QPalette::Button);
      if (pressed)
        return opt->palette.color(QPalette::Active, QPalette::Light);
      if (hover || focus)
        return opt->palette.color(QPalette::Active, QPalette::Button);
      return opt->palette.color(QPalette::Inactive, QPalette::Button);
    }();

    auto const borderColor = [&]{
      if (!enabled)
        return opt->palette.color(QPalette::Disabled, QPalette::Light);
      if (pressed || focus)
        return opt->palette.color(QPalette::Active, QPalette::Dark);
      if (hover)
        return opt->palette.color(QPalette::Active, QPalette::Light);
      return opt->palette.color(QPalette::Inactive, QPalette::Light);
    }();

    p->setRenderHint(QPainter::Antialiasing);
    p->setBrush(fillColor);
    p->setPen(QPen{borderColor, 1});

    auto const ar = QRectF{opt->rect}.adjusted(+0.5, +0.5, -0.5, -0.5);
    p->drawRoundedRect(ar, RADIUS, RADIUS);

    // TODO inner shadow (o = 0.125) when pressed
  }

  // --------------------------------------------------------------------------
  void drawButton(
    QStyleOptionButton const* opt, QPainter* p, QWidget const* button)
  {
    drawButtonFrame(opt, p);

    constexpr auto flags =
      Qt::AlignHCenter | Qt::AlignVCenter | Qt::TextShowMnemonic;

    if (button)
      p->setFont(button->font());
    p->setPen(buttonTextColor(opt));

    constexpr auto m = BUTTON_MARGIN;
    auto rect = opt->rect.adjusted(+m, +m, -m, -m);
    auto const icon = pulseIcon(button);
    if (!icon.isNull())
    {
      constexpr auto iconFlags = Qt::AlignHCenter | Qt::AlignTop;

      p->setFont(iconFont(button, BUTTON_ICON_SIZE));
      p->drawText(rect, iconFlags, icon);
      p->setFont(button->font());

      rect.adjust(0, BUTTON_ICON_SIZE, 0, 0);
    }
    p->drawText(rect, flags, opt->text);
  }

  // --------------------------------------------------------------------------
  void drawToolButton(
    QStyleOptionToolButton const* opt, QPainter* p, QToolButton const* button)
  {
    constexpr auto flags =
      Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic;

    p->setPen(paletteColor(opt, foreground(button, QPalette::ButtonText)));

    if (button)
    {
      auto const icon = pulseIcon(button);
      if (!icon.isNull())
      {
        constexpr auto iconFlags = Qt::AlignHCenter | Qt::AlignVCenter;

        p->setFont(iconFont(button));
        p->drawText(opt->rect, iconFlags, icon);

        return;
      }
    }

    if (!opt->text.isEmpty())
    {
      if (button)
        p->setFont(button->font());

      p->drawText(opt->rect, flags, opt->text);
    }
  }

  // --------------------------------------------------------------------------
  void drawSidebarButton(
    QStyleOptionButton const* opt, QPainter* p, QPushButton const* button)
  {
    if (opt->state & QStyle::State_Enabled &&
        opt->state & QStyle::State_MouseOver)
    {
      auto const role = background(button, QPalette::Button);
      auto const& brush = opt->palette.color(QPalette::Active, role);
      p->fillRect(opt->rect, brush);
    }

    auto const cg = [s = opt->state]{
      if (!(s & QStyle::State_Enabled))
        return QPalette::Disabled;
      if (s & QStyle::State_MouseOver)
        return QPalette::Active;
      return QPalette::Inactive;
    }();

    constexpr auto m = BUTTON_MARGIN;
    constexpr auto iw = SIDEBAR_BUTTON_ICON_SIZE;
    constexpr auto im = iw + (2 * m);
    auto textRect = opt->rect.adjusted(+im, +m, -im, -m);

    constexpr auto flags =
      Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic;

    if (button)
      p->setFont(button->font());

    p->setPen(
      opt->palette.color(cg, foreground(button, QPalette::ButtonText)));
    p->drawText(textRect, flags, opt->text);

    if (button)
    {
      constexpr auto iconFlags = Qt::AlignHCenter | Qt::AlignVCenter;

      if (button->isCheckable())
      {
        auto const c =
          QChar{opt->state & QStyle::State_On ? u'\uf106' : u'\uf107'};

        auto checkRect = opt->rect.adjusted(+m, +m, -m, -m);
        checkRect.setLeft(checkRect.right() - iw);

        p->setFont(iconFont(button));
        p->drawText(checkRect, iconFlags, c);
      }

      auto const icon = pulseIcon(button);
      if (!icon.isNull())
      {
        auto iconRect = opt->rect.adjusted(+m, +m, -m, -m);
        iconRect.setWidth(iw);

        p->setFont(iconFont(button));
        p->drawText(iconRect, iconFlags, icon);
      }
    }
  }

  // --------------------------------------------------------------------------
  void drawViewPanel(
    QStyleOptionFrame const* opt, QPainter* p, QWidget const* w)
  {
    Q_UNUSED(w)

    auto const ar = QRectF{opt->rect}.adjusted(+0.5, +0.5, -0.5, -0.5);
    p->setRenderHint(QPainter::Antialiasing);

    // Generate inner shadow
    auto fill = pixmapBuffer(p, opt->rect.size());

    auto const fillColor = paletteColor(opt, background(w, QPalette::Base));
    auto const shadowColor = paletteColor(opt, QPalette::Shadow);
    fill.fill(qtColorUtil::blend(fillColor, shadowColor, 0.075));

    QPainter pp{&fill};
    pp.setRenderHint(QPainter::Antialiasing);
    pp.setPen({});
    pp.setBrush(fillColor);
    pp.drawRoundedRect(
      ar.adjusted(+0.5, +1.5, -0.5, -0.5), RADIUS - 1, RADIUS - 1);

    // Paint frame
    auto const focus = !!(opt->state & QStyle::State_HasFocus);
    auto const border =
      (focus ? QColor{102, 175, 233} : paletteColor(opt, QPalette::Mid));
    // TODO focus outer shadow 0 0 8px rgba(102,175,233,0.6);

    p->setPen({border, 1});
    p->setBrush(fill);
    p->drawRoundedRect(ar, RADIUS, RADIUS);
  }
}

// ----------------------------------------------------------------------------
WidgetStyle::WidgetStyle(QObject* parent)
{
  // Parent classes don't accept a QObject parent; have to set it explicitly
  setParent(parent);
}

// ----------------------------------------------------------------------------
WidgetStyle::~WidgetStyle()
{
}

// ----------------------------------------------------------------------------
void WidgetStyle::drawPrimitive(
  PrimitiveElement pe, QStyleOption const* opt,
  QPainter* p, QWidget const* w) const
{
  switch (pe)
  {
    case PE_PanelLineEdit:
      if (auto* const fo = cast<QStyleOptionFrame>(opt))
      {
        if (auto* le = qobject_cast<QLineEdit const*>(w))
        {
          if (le->hasFrame())
            drawViewPanel(fo, p, w);
          return;
        }
      }
      break;

    case PE_IndicatorSpinUp:
      drawGlyph(p, w, opt->rect, buttonTextColor(opt),
                QStringLiteral(u"\uf105"));
      return;

    case PE_IndicatorSpinDown:
      drawGlyph(p, w, opt->rect, buttonTextColor(opt),
                QStringLiteral(u"\uf107"));
      return;

    case PE_IndicatorCheckBox:
      if (auto* const bo = cast<QStyleOptionButton>(opt))
      {
        drawButtonFrame(bo, p);
        if (opt->state & (State_On | State_NoChange))
        {
          auto color = buttonTextColor(opt);
          if (opt->state & State_NoChange)
            color.setAlphaF(0.5);

          auto const& r = opt->rect.adjusted(CHECK_OFFSET, 0, 0, 0);
          drawGlyph(p, w, r, color, QStringLiteral(u"\uf100"));
        }
        return;
      }
      break;

    default:
      break;
  }

  QCommonStyle::drawPrimitive(pe, opt, p, w);
}

// ----------------------------------------------------------------------------
void WidgetStyle::drawControl(
  ControlElement ce, QStyleOption const* opt,
  QPainter* p, QWidget const* w) const
{
  switch (ce)
  {
    case CE_PushButton:
      if (auto* const bo = cast<QStyleOptionButton>(opt))
      {
        auto* const button = qobject_cast<QPushButton const*>(w);
        if (bo->features & QStyleOptionButton::Flat)
          drawSidebarButton(bo, p, button);
        else
          drawButton(bo, p, button);
        return;
      }
      break;

    case CE_CheckBox:
      if (auto* const bo = cast<QStyleOptionButton>(opt))
      {
        auto io = *bo;
        io.rect = subElementRect(SE_CheckBoxIndicator, opt, w);
        drawPrimitive(PE_IndicatorCheckBox, &io, p, w);

        auto const& lr = subElementRect(SE_CheckBoxContents, opt, w);
        constexpr auto flags =
          Qt::AlignLeft | Qt::AlignVCenter | Qt::TextShowMnemonic;

        if (w)
          p->setFont(w->font());
        p->setPen(buttonTextColor(opt, foreground(w, QPalette::WindowText)));

        p->drawText(lr, flags, bo->text);

        return;
      }
      break;

    case CE_ShapedFrame:
      if (auto* const fo = cast<QStyleOptionFrame>(opt))
      {
        if (fo->frameShape == QFrame::StyledPanel)
          drawViewPanel(fo, p, w);
        else
          drawFrame(fo, p, w);
        return;
      }
      break;

    case CE_ToolBar:
      p->fillRect(
        opt->rect, paletteColor(opt, background(w, QPalette::Window)));
      return;

    case CE_SizeGrip:
      return; // not rendered

    default:
      break;
  }

  QCommonStyle::drawControl(ce, opt, p, w);
}

// ----------------------------------------------------------------------------
void WidgetStyle::drawComplexControl(
  ComplexControl cc, QStyleOptionComplex const* opt, QPainter* p,
  QWidget const* w) const
{
  switch (cc)
  {
    case CC_SpinBox:
      if (auto* const so = cast<QStyleOptionSpinBox>(opt))
      {
        // Frame
        if (so->frame)
        {
          auto fo = QStyleOptionFrame{};
          static_cast<QStyleOption&>(fo) = *opt;
          fo.frameShape = QFrame::StyledPanel;
          drawViewPanel(&fo, p, w);
        }

        // Up button
        auto ubo = *so;
        ubo.rect = subControlRect(CC_SpinBox, so, SC_SpinBoxUp, w);
        if (!(so->stepEnabled & QAbstractSpinBox::StepUpEnabled))
          ubo.state &= ~State_Enabled;
        if (so->activeSubControls == SC_SpinBoxUp &&
            (so->state & State_Sunken))
        {
          ubo.state |= State_On | State_Sunken;
        }
        else
        {
          ubo.state |= State_Raised;
          ubo.state &= ~State_Sunken;
        }
        drawPrimitive(PE_IndicatorSpinUp, &ubo, p, w);

        // Down button
        auto dbo = *so;
        dbo.rect = subControlRect(CC_SpinBox, so, SC_SpinBoxDown, w);
        if (!(so->stepEnabled & QAbstractSpinBox::StepDownEnabled))
          dbo.state &= ~State_Enabled;
        if (so->activeSubControls == SC_SpinBoxDown &&
            (so->state & State_Sunken))
        {
          dbo.state |= State_On | State_Sunken;
        }
        else
        {
          dbo.state |= State_Raised;
          dbo.state &= ~State_Sunken;
        }
        drawPrimitive(PE_IndicatorSpinDown, &dbo, p, w);

        return;
      }
      break;

    case CC_ComboBox:
      if (auto* const co = cast<QStyleOptionComboBox>(opt))
      {
        if (co->editable)
        {
          if (co->frame)
          {
            auto fo = QStyleOptionFrame{};
            static_cast<QStyleOption&>(fo) = *opt;
            fo.frameShape = QFrame::StyledPanel;

            drawViewPanel(&fo, p, w);
          }

          p->setPen(paletteColor(opt, foreground(w, QPalette::Text)));
        }
        else
        {
          if (co->frame)
          {
            auto bo = QStyleOptionButton{};
            static_cast<QStyleOption&>(bo) = *opt;
            drawButton(&bo, p, w);
          }

          p->setPen(buttonTextColor(opt));
        }

        p->setFont(iconFont(w));

        auto const& r = subControlRect(cc, opt, SC_ComboBoxArrow, w);
        constexpr auto flags = Qt::AlignHCenter | Qt::AlignVCenter;
        p->drawText(r, flags, QStringLiteral(u"\uf107"));

        if (w)
          p->setFont(w->font());

        return;
      }
      break;

    case CC_ToolButton:
      drawToolButton(static_cast<QStyleOptionToolButton const*>(opt),
                     p, qobject_cast<QToolButton const*>(w));
      return;

    default:
      break;
  }

  QCommonStyle::drawComplexControl(cc, opt, p, w);
}

// ----------------------------------------------------------------------------
QRect WidgetStyle::subControlRect(
  ComplexControl cc, QStyleOptionComplex const* opt,
  SubControl sc, const QWidget* w) const
{
  switch (cc)
  {
    case CC_ComboBox:
      switch (sc)
      {
        case SC_ComboBoxArrow:
          if (auto* const co = cast<QStyleOptionComboBox>(opt))
          {
            auto const fw = co->frame ? FRAME_WIDTH : 0;
            auto r = co->rect.adjusted(+fw, +fw, -fw, -fw);
            auto const w = std::max(16, (r.height() * 4) / 5);
            r.setLeft(r.right() - w);
            return r;
          }
          break;

        default:
          break;
      }
      break;

    default:
      break;
  }

  return QCommonStyle::subControlRect(cc, opt, sc, w);
}

// ----------------------------------------------------------------------------
QRect WidgetStyle::subElementRect(
  SubElement se, QStyleOption const* opt, QWidget const* w) const
{
  switch (se)
  {
    case QStyle::SE_ShapedFrameContents:
      if (auto* const fo = cast<QStyleOptionFrame>(opt))
      {
        if (fo->frameShape == QFrame::StyledPanel)
          return opt->rect.adjusted(1, 1, -1, -1);
      }
      break;

    default:
      break;
  }

  return QCommonStyle::subElementRect(se, opt, w);
}

// ----------------------------------------------------------------------------
QSize WidgetStyle::sizeFromContents(
  ContentsType ct, QStyleOption const* opt,
  QSize const& cs, QWidget const* w) const
{
  switch (ct)
  {
    case CT_PushButton:
      if (auto* const bo = cast<QStyleOptionButton>(opt))
      {
        if (bo->features & QStyleOptionButton::Flat)
        {
          constexpr auto m = SIDEBAR_BUTTON_MARGIN;
          return cs + QSize{(2 * SIDEBAR_BUTTON_ICON_SIZE) + (6 * m), 2 * m};
        }

        auto bs = cs;
        if (!pulseIcon(w).isNull())
        {
          bs.setWidth(std::max(bs.width(), BUTTON_ICON_SIZE));
          bs += QSize{0, BUTTON_ICON_SIZE};
        }

        return bs + QSize{5 * BUTTON_MARGIN, 2 * BUTTON_MARGIN};
      }
      return cs + QSize{5 * BUTTON_MARGIN, 2 * BUTTON_MARGIN};

    case CT_SpinBox:
      if (auto* const so = cast<QStyleOptionSpinBox>(opt))
      {
        auto const& ad = subControlRect(CC_SpinBox, so, SC_SpinBoxDown, w);
        auto const& au = subControlRect(CC_SpinBox, so, SC_SpinBoxUp, w);
        auto const aw = (au.united(ad)).width();

        if (!so->frame)
          return cs + QSize{LINEEDIT_MARGIN + aw, 0};

        return cs + QSize{(3 * LINEEDIT_MARGIN) + aw, 2 * LINEEDIT_MARGIN};
      }

      return cs + QSize{(3 * LINEEDIT_MARGIN) + cs.height(), 2 * LINEEDIT_MARGIN};

    case CT_ToolButton:
    {
      if (!pulseIcon(w).isNull())
        return QSize{SIDEBAR_BUTTON_ICON_SIZE, SIDEBAR_BUTTON_ICON_SIZE};
      break;
    }

    case CT_LineEdit:
      if (auto* const fo = cast<QStyleOptionFrame>(opt))
      {
        if (fo->frameShape == QFrame::NoFrame)
          return cs;
      }

      return cs + QSize{2 * LINEEDIT_MARGIN, 2 * LINEEDIT_MARGIN};

    case CT_ComboBox:
      if (auto* const co = cast<QStyleOptionComboBox>(opt))
      {
        auto const& a = subControlRect(CC_ComboBox, co, SC_ComboBoxArrow, w);
        auto const aw = a.width();

        if (!co->frame)
          return cs + QSize{LINEEDIT_MARGIN + aw, 0};

        if (co->editable)
          return cs + QSize{(3 * LINEEDIT_MARGIN) + aw, 2 * LINEEDIT_MARGIN};

        return cs + QSize{(3 * BUTTON_MARGIN) + aw, 2 * BUTTON_MARGIN};
      }

      return cs + QSize{(3 * BUTTON_MARGIN) + cs.height(), 2 * BUTTON_MARGIN};

    default:
      break;
  }

  return QCommonStyle::sizeFromContents(ct, opt, cs, w);
}

// ----------------------------------------------------------------------------
int WidgetStyle::pixelMetric(
  PixelMetric m, QStyleOption const* opt, QWidget const* w) const
{
  switch (m)
  {
    case PM_ButtonMargin:
      if (auto* const bo = cast<QStyleOptionButton>(opt))
      {
        if (bo->features & QStyleOptionButton::Flat)
          return SIDEBAR_BUTTON_MARGIN;
        return BUTTON_MARGIN;
      }
      break;

    case PM_IndicatorWidth:
    case PM_IndicatorHeight:
      return CHECKBOX_SIZE;

    case PM_CheckBoxLabelSpacing:
      return CHECKBOX_MARGIN;

    case PM_DefaultFrameWidth:
      return FRAME_WIDTH;

    default:
      break;
  }

  return QCommonStyle::pixelMetric(m, opt, w);
}

// ----------------------------------------------------------------------------
int WidgetStyle::styleHint(
  QStyle::StyleHint sh, const QStyleOption* opt, const QWidget* w,
  QStyleHintReturn* shret) const
{
  switch (sh)
  {
    case SH_FormLayoutLabelAlignment:
      return Qt::AlignRight | Qt::AlignVCenter;

    case SH_FormLayoutFieldGrowthPolicy:
      return QFormLayout::ExpandingFieldsGrow;

    case SH_ComboBox_ListMouseTracking:
    case SH_Menu_MouseTracking:
      return true;

    default:
      break;
  }

  return QCommonStyle::styleHint(sh, opt, w, shret);
}

// ----------------------------------------------------------------------------
void WidgetStyle::polish(QWidget *widget)
{
  setFormLayoutMetrics(widget->layout());

  // Enable mouse tracking for widgets with hover effects
  if (qobject_cast<QAbstractButton*>(widget))
    widget->setAttribute(Qt::WA_Hover);
  else if (auto* const cb = qobject_cast<QComboBox*>(widget))
  {
    if (!cb->isEditable())
    {
      widget->setAttribute(Qt::WA_Hover);
    }
    else
    {
      widget->setBackgroundRole(QPalette::Base);
      widget->setForegroundRole(QPalette::Text);
    }
  }

  // Set font weight for input widgets
  if (qobject_cast<QComboBox*>(widget))
    setFontWeight(widget, 50);
  if (qobject_cast<QLineEdit*>(widget))
    setFontWeight(widget, 50);
  if (qobject_cast<QAbstractSpinBox*>(widget))
    setFontWeight(widget, 50);
}
