View Issue Details

IDProjectCategoryView StatusLast Update
0017522ScribusUser Interfacepublic2025-12-13 22:27
Reporternitramr Assigned Tonitramr  
PrioritynormalSeverityfeatureReproducibilityN/A
Status assignedResolutionopen 
PlatformDesktop PCOSUbuntuOS Version24.10 64-bit
Product Version1.7.1.svn 
Target Version1.7 milestone 
Summary0017522: New Color Slider widget
DescriptionI made a new color slider that has the render logic inside the widget.
In short term, it will replace the existing ScColorSlider.

Supported Color Modes:
- RGB
- RGB Web
- CMYK
- Lab
- HLC
- HSV <- new
- Shade <- new
- Alpha <- new

The patch implements the new color slider in color picker for alpha and shade settings.
To replace the color slider in cmykfw widget, it takes more time.
TagsNo tags attached.
Attached Files
color_slider.zip (5,944 bytes)
colorslider_2025-05-13_01.patch (10,348 bytes)   
Index: scribus/CMakeLists_Sources.txt
===================================================================
--- scribus/CMakeLists_Sources.txt	(Revision 26867)
+++ scribus/CMakeLists_Sources.txt	(Arbeitskopie)
@@ -562,6 +562,7 @@
 	ui/docks/dock_centralwidget.cpp
 	ui/docks/dock_panelbase.cpp
 	ui/widgets/color_button.cpp
+	ui/widgets/color_slider.cpp
 	ui/widgets/combo_blendmode.cpp
 	ui/widgets/combo_linestyle.cpp
 	ui/widgets/dropdown_button.cpp
Index: scribus/ui/colorpicker/colorpicker_color.cpp
===================================================================
--- scribus/ui/colorpicker/colorpicker_color.cpp	(Revision 26867)
+++ scribus/ui/colorpicker/colorpicker_color.cpp	(Arbeitskopie)
@@ -26,6 +26,9 @@
 
 	languageChange();
 
+	sliderAlpha->setMode(ColorSlider::Opacity);
+	sliderShade->setMode(ColorSlider::Shade);
+
 	connectSlots();
 	connect(sectionSwatches,    &SectionContainer::sizeChanged,				this, &ColorPickerColor::updateSize);
 }
@@ -34,7 +37,9 @@
 {
 	connect(swatches,			&ColorPickerColorSwatches::colorChanged,	this, &ColorPickerColor::updateColorFromSwatches);	
 	connect(numberShade,		&ScrSpinBox::valueChanged,					this, &ColorPickerColor::updateColorShade);
+	connect(sliderShade,		&ColorSlider::valueChanged,					this, &ColorPickerColor::updateColorShade);
 	connect(numberAlpha,		&ScrSpinBox::valueChanged,					this, &ColorPickerColor::updateColorAlpha);
+	connect(sliderAlpha,		&ColorSlider::valueChanged,					this, &ColorPickerColor::updateColorAlpha);
 }
 
 void ColorPickerColor::disconnectSlots()
@@ -41,7 +46,9 @@
 {
 	disconnect(swatches,		&ColorPickerColorSwatches::colorChanged,	this, &ColorPickerColor::updateColorFromSwatches);
 	disconnect(numberShade,		&ScrSpinBox::valueChanged,					this, &ColorPickerColor::updateColorShade);
+	disconnect(sliderShade,		&ColorSlider::valueChanged,					this, &ColorPickerColor::updateColorShade);
 	disconnect(numberAlpha,		&ScrSpinBox::valueChanged,					this, &ColorPickerColor::updateColorAlpha);
+	disconnect(sliderAlpha,		&ColorSlider::valueChanged,					this, &ColorPickerColor::updateColorAlpha);
 }
 
 /* ********************************************************************************* *
@@ -73,8 +80,10 @@
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(false);
 		labelShade->setVisible(false);
+		sliderShade->setVisible(false);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	case Context::Text:
 	case Context::TextBackground:
@@ -82,22 +91,28 @@
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(true);
 		labelShade->setVisible(true);
+		sliderShade->setVisible(true);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	case Context::Fill:
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(true);
 		labelShade->setVisible(true);
+		sliderShade->setVisible(true);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	case Context::Line:
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(true);
 		labelShade->setVisible(true);
+		sliderShade->setVisible(true);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	case Context::FillMask:
 	case Context::LineMask:
@@ -104,8 +119,10 @@
 		sectionSwatches->setVisible(false);
 		numberShade->setVisible(false);
 		labelShade->setVisible(false);
+		sliderShade->setVisible(false);
 		numberAlpha->setVisible(true);
 		labelAlpha->setVisible(true);
+		sliderAlpha->setVisible(true);
 		isMask = true;
 		break;
 	case Context::DropShadow:
@@ -112,8 +129,10 @@
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(true);
 		labelShade->setVisible(true);
+		sliderShade->setVisible(true);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	}
 
@@ -183,12 +202,19 @@
 void ColorPickerColor::setDoc(ScribusDoc *doc)
 {
 	m_doc = doc;
+	sliderAlpha->setDoc(m_doc);
+	sliderShade->setDoc(m_doc);
 }
 
 void ColorPickerColor::setColorData(const CPColorData& color)
 {
+	if (!m_doc)
+		return;
+
 	m_color = color;
 
+	bool hasColor = m_color.Name != CommonStrings::tr_NoneColor;
+
 	// update UI + controls
 	QSignalBlocker sigSwatches(swatches);
 	swatches->setCurrentColor(m_color.Name);
@@ -195,12 +221,20 @@
 
 	QSignalBlocker sigShade(numberShade);
 	numberShade->setValue(m_color.Shade);
-	numberShade->setEnabled(m_color.Name != CommonStrings::tr_NoneColor);
+	numberShade->setEnabled(hasColor);
 
 	QSignalBlocker sigAlpha(numberAlpha);
 	numberAlpha->setValue(qRound(100 - (m_color.Opacity * 100)));
-	numberAlpha->setEnabled(m_color.Name != CommonStrings::tr_NoneColor);
+	numberAlpha->setEnabled(hasColor);
 
+	QSignalBlocker sigAlphaSlider(sliderAlpha);
+	sliderAlpha->setEnabled(hasColor);
+	sliderAlpha->setValues(m_color.Name, numberAlpha->value(), numberShade->value());
+
+	QSignalBlocker sigShadeSlider(sliderShade);
+	sliderShade->setEnabled(hasColor);
+	sliderShade->setValues(m_color.Name, numberAlpha->value(), numberShade->value());
+
 }
 
 void ColorPickerColor::languageChange()
@@ -211,14 +245,33 @@
 void ColorPickerColor::updateColorFromSwatches()
 {
 	m_color.Name = swatches->currentColor();
-	numberShade->setEnabled(m_color.Name != CommonStrings::tr_NoneColor);
-	numberAlpha->setEnabled(m_color.Name != CommonStrings::tr_NoneColor);
 
+	bool hasColor = m_color.Name != CommonStrings::tr_NoneColor;
+
+	numberShade->setEnabled(hasColor);
+	sliderShade->setEnabled(hasColor);
+	sliderShade->setValues(m_color.Name, numberAlpha->value(), numberShade->value());
+	numberAlpha->setEnabled(hasColor);
+	sliderAlpha->setEnabled(hasColor);
+	sliderAlpha->setValues(m_color.Name, numberAlpha->value(), numberShade->value());
+
 	updateColor();
 }
 
 void ColorPickerColor::updateColorShade()
 {
+	QObject* obj = sender();
+	if( obj == sliderShade )
+	{
+		QSignalBlocker sigBlocker(numberShade);
+		numberShade->setValue(sliderShade->value());
+	}
+	else if( obj == numberShade )
+	{
+		QSignalBlocker sigBlocker(sliderShade);
+		sliderShade->setValue(numberShade->value());
+	}
+
 	m_color.Shade = numberShade->value();
 	updateColor();
 }
@@ -225,6 +278,18 @@
 
 void ColorPickerColor::updateColorAlpha()
 {
+	QObject* obj = sender();
+	if( obj == sliderAlpha )
+	{
+		QSignalBlocker sigBlocker(numberAlpha);
+		numberAlpha->setValue(sliderAlpha->value());
+	}
+	else if( obj == numberAlpha )
+	{
+		QSignalBlocker sigBlocker(sliderAlpha);
+		sliderAlpha->setValue(numberAlpha->value());
+	}
+
 	m_color.Opacity = (100 - numberAlpha->value()) / 100.0;
 	updateColor();
 }
@@ -242,5 +307,3 @@
 	resize(w, sizeHint().height());
 	emit sizeChanged();
 }
-
-
Index: scribus/ui/colorpicker/colorpicker_color.ui
===================================================================
--- scribus/ui/colorpicker/colorpicker_color.ui	(Revision 26867)
+++ scribus/ui/colorpicker/colorpicker_color.ui	(Arbeitskopie)
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>300</width>
-    <height>122</height>
+    <height>112</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -46,8 +46,28 @@
      <property name="spacing">
       <number>4</number>
      </property>
-     <item row="1" column="2">
-      <widget class="ScrSpinBox" name="numberAlpha">
+     <item row="0" column="1">
+      <widget class="ColorSlider" name="sliderShade">
+       <property name="orientation">
+        <enum>Qt::Orientation::Horizontal</enum>
+       </property>
+       <property name="tickPosition">
+        <enum>QSlider::TickPosition::TicksAbove</enum>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="ColorSlider" name="sliderAlpha">
+       <property name="orientation">
+        <enum>Qt::Orientation::Horizontal</enum>
+       </property>
+       <property name="tickPosition">
+        <enum>QSlider::TickPosition::TicksAbove</enum>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="2">
+      <widget class="ScrSpinBox" name="numberShade">
        <property name="suffix">
         <string> %</string>
        </property>
@@ -59,28 +79,28 @@
        </property>
       </widget>
      </item>
-     <item row="1" column="1">
+     <item row="1" column="0">
       <widget class="QLabel" name="labelAlpha">
        <property name="text">
         <string>Opacity:</string>
        </property>
        <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
        </property>
       </widget>
      </item>
-     <item row="0" column="1">
+     <item row="0" column="0">
       <widget class="QLabel" name="labelShade">
        <property name="text">
         <string>Shade:</string>
        </property>
        <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
        </property>
       </widget>
      </item>
-     <item row="0" column="2">
-      <widget class="ScrSpinBox" name="numberShade">
+     <item row="1" column="2">
+      <widget class="ScrSpinBox" name="numberAlpha">
        <property name="suffix">
         <string> %</string>
        </property>
@@ -92,19 +112,6 @@
        </property>
       </widget>
      </item>
-     <item row="1" column="0">
-      <spacer name="horizontalSpacer">
-       <property name="orientation">
-        <enum>Qt::Horizontal</enum>
-       </property>
-       <property name="sizeHint" stdset="0">
-        <size>
-         <width>0</width>
-         <height>0</height>
-        </size>
-       </property>
-      </spacer>
-     </item>
     </layout>
    </item>
    <item>
@@ -136,6 +143,11 @@
    <header>ui/colorpicker/colorpicker_color_swatches.h</header>
    <container>1</container>
   </customwidget>
+  <customwidget>
+   <class>ColorSlider</class>
+   <extends>QSlider</extends>
+   <header>ui/widgets/color_slider.h</header>
+  </customwidget>
  </customwidgets>
  <tabstops>
   <tabstop>numberShade</tabstop>
colorslider_2025-05-13_01.patch (10,348 bytes)   
preview.png (18,821 bytes)   
preview.png (18,821 bytes)   
PatchYes

Relationships

duplicate of 0006434 assignedfschmid Implement sliders as control for values such as opacity 
child of 0017540 new Metabug: UI fixes for the release of Scribus 1.8 

Activities

cbradney

2025-05-13 21:06

administrator   ~0052524

Not sure what's different on Mac. No numbers on the slider.. slider is higher
image.png (20,951 bytes)   
image.png (20,951 bytes)   

nitramr

2025-10-25 14:16

developer   ~0053120

@cbradney I fixed the style issue.

The new patch requires the ScribusProxyStyle class from this patch:
https://bugs.scribus.net/view.php?id=17571
colorslider_2025-10-25_01.patch (21,671 bytes)   
Index: scribus/CMakeLists_Sources.txt
===================================================================
--- scribus/CMakeLists_Sources.txt	(Revision 27094)
+++ scribus/CMakeLists_Sources.txt	(Arbeitskopie)
@@ -565,7 +565,8 @@
 	ui/delegates/sclistitemdelegate.cpp
 	ui/docks/dock_centralwidget.cpp
 	ui/docks/dock_panelbase.cpp
	ui/factories/scribusproxystyle.cpp
	ui/widgets/buttongroup.cpp
 	ui/widgets/color_button.cpp
+	ui/widgets/color_slider.cpp
 	ui/widgets/combo_blendmode.cpp
 	ui/widgets/combo_linestyle.cpp
 	ui/widgets/dropdown_button.cpp
Index: scribus/ui/colorpicker/colorpicker_color.cpp
===================================================================
--- scribus/ui/colorpicker/colorpicker_color.cpp	(Revision 27094)
+++ scribus/ui/colorpicker/colorpicker_color.cpp	(Arbeitskopie)
@@ -26,6 +26,9 @@
 
 	languageChange();
 
+	sliderAlpha->setMode(ColorSlider::Opacity);
+	sliderShade->setMode(ColorSlider::Shade);
+
 	connectSlots();
 	connect(sectionSwatches,    &SectionContainer::sizeChanged,				this, &ColorPickerColor::updateSize);
 }
@@ -34,7 +37,9 @@
 {
 	connect(swatches,			&ColorPickerColorSwatches::colorChanged,	this, &ColorPickerColor::updateColorFromSwatches);	
 	connect(numberShade,		&ScrSpinBox::valueChanged,					this, &ColorPickerColor::updateColorShade);
+	connect(sliderShade,		&ColorSlider::valueChanged,					this, &ColorPickerColor::updateColorShade);
 	connect(numberAlpha,		&ScrSpinBox::valueChanged,					this, &ColorPickerColor::updateColorAlpha);
+	connect(sliderAlpha,		&ColorSlider::valueChanged,					this, &ColorPickerColor::updateColorAlpha);
 }
 
 void ColorPickerColor::disconnectSlots()
@@ -41,7 +46,9 @@
 {
 	disconnect(swatches,		&ColorPickerColorSwatches::colorChanged,	this, &ColorPickerColor::updateColorFromSwatches);
 	disconnect(numberShade,		&ScrSpinBox::valueChanged,					this, &ColorPickerColor::updateColorShade);
+	disconnect(sliderShade,		&ColorSlider::valueChanged,					this, &ColorPickerColor::updateColorShade);
 	disconnect(numberAlpha,		&ScrSpinBox::valueChanged,					this, &ColorPickerColor::updateColorAlpha);
+	disconnect(sliderAlpha,		&ColorSlider::valueChanged,					this, &ColorPickerColor::updateColorAlpha);
 }
 
 /* ********************************************************************************* *
@@ -73,8 +80,10 @@
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(false);
 		labelShade->setVisible(false);
+		sliderShade->setVisible(false);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	case Context::Text:
 	case Context::TextBackground:
@@ -82,22 +91,28 @@
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(true);
 		labelShade->setVisible(true);
+		sliderShade->setVisible(true);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	case Context::Fill:
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(true);
 		labelShade->setVisible(true);
+		sliderShade->setVisible(true);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	case Context::Line:
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(true);
 		labelShade->setVisible(true);
+		sliderShade->setVisible(true);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	case Context::FillMask:
 	case Context::LineMask:
@@ -104,8 +119,10 @@
 		sectionSwatches->setVisible(false);
 		numberShade->setVisible(false);
 		labelShade->setVisible(false);
+		sliderShade->setVisible(false);
 		numberAlpha->setVisible(true);
 		labelAlpha->setVisible(true);
+		sliderAlpha->setVisible(true);
 		isMask = true;
 		break;
 	case Context::DropShadow:
@@ -112,8 +129,10 @@
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(true);
 		labelShade->setVisible(true);
+		sliderShade->setVisible(true);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	}
 
@@ -183,12 +202,19 @@
 void ColorPickerColor::setDoc(ScribusDoc *doc)
 {
 	m_doc = doc;
+	sliderAlpha->setDoc(m_doc);
+	sliderShade->setDoc(m_doc);
 }
 
 void ColorPickerColor::setColorData(const CPColorData& color)
 {
+	if (!m_doc)
+		return;
+
 	m_color = color;
 
+	bool hasColor = m_color.Name != CommonStrings::tr_NoneColor;
+
 	// update UI + controls
 	QSignalBlocker sigSwatches(swatches);
 	swatches->setCurrentColor(m_color.Name);
@@ -195,12 +221,20 @@
 
 	QSignalBlocker sigShade(numberShade);
 	numberShade->setValue(m_color.Shade);
-	numberShade->setEnabled(m_color.Name != CommonStrings::tr_NoneColor);
+	numberShade->setEnabled(hasColor);
 
 	QSignalBlocker sigAlpha(numberAlpha);
 	numberAlpha->setValue(qRound(100 - (m_color.Opacity * 100)));
-	numberAlpha->setEnabled(m_color.Name != CommonStrings::tr_NoneColor);
+	numberAlpha->setEnabled(hasColor);
 
+	QSignalBlocker sigAlphaSlider(sliderAlpha);
+	sliderAlpha->setEnabled(hasColor);
+	sliderAlpha->setValues(m_color.Name, numberAlpha->value(), numberShade->value());
+
+	QSignalBlocker sigShadeSlider(sliderShade);
+	sliderShade->setEnabled(hasColor);
+	sliderShade->setValues(m_color.Name, numberAlpha->value(), numberShade->value());
+
 }
 
 void ColorPickerColor::languageChange()
@@ -211,14 +245,33 @@
 void ColorPickerColor::updateColorFromSwatches()
 {
 	m_color.Name = swatches->currentColor();
-	numberShade->setEnabled(m_color.Name != CommonStrings::tr_NoneColor);
-	numberAlpha->setEnabled(m_color.Name != CommonStrings::tr_NoneColor);
 
+	bool hasColor = m_color.Name != CommonStrings::tr_NoneColor;
+
+	numberShade->setEnabled(hasColor);
+	sliderShade->setEnabled(hasColor);
+	sliderShade->setValues(m_color.Name, numberAlpha->value(), numberShade->value());
+	numberAlpha->setEnabled(hasColor);
+	sliderAlpha->setEnabled(hasColor);
+	sliderAlpha->setValues(m_color.Name, numberAlpha->value(), numberShade->value());
+
 	updateColor();
 }
 
 void ColorPickerColor::updateColorShade()
 {
+	QObject* obj = sender();
+	if( obj == sliderShade )
+	{
+		QSignalBlocker sigBlocker(numberShade);
+		numberShade->setValue(sliderShade->value());
+	}
+	else if( obj == numberShade )
+	{
+		QSignalBlocker sigBlocker(sliderShade);
+		sliderShade->setValue(numberShade->value());
+	}
+
 	m_color.Shade = numberShade->value();
 	updateColor();
 }
@@ -225,6 +278,18 @@
 
 void ColorPickerColor::updateColorAlpha()
 {
+	QObject* obj = sender();
+	if( obj == sliderAlpha )
+	{
+		QSignalBlocker sigBlocker(numberAlpha);
+		numberAlpha->setValue(sliderAlpha->value());
+	}
+	else if( obj == numberAlpha )
+	{
+		QSignalBlocker sigBlocker(sliderAlpha);
+		sliderAlpha->setValue(numberAlpha->value());
+	}
+
 	m_color.Opacity = (100 - numberAlpha->value()) / 100.0;
 	updateColor();
 }
@@ -242,5 +307,3 @@
 	resize(w, sizeHint().height());
 	emit sizeChanged();
 }
-
-
Index: scribus/ui/colorpicker/colorpicker_color.ui
===================================================================
--- scribus/ui/colorpicker/colorpicker_color.ui	(Revision 27094)
+++ scribus/ui/colorpicker/colorpicker_color.ui	(Arbeitskopie)
@@ -7,7 +7,7 @@
     <x>0</x>
     <y>0</y>
     <width>300</width>
-    <height>122</height>
+    <height>112</height>
    </rect>
   </property>
   <property name="windowTitle">
@@ -46,8 +46,28 @@
      <property name="spacing">
       <number>4</number>
      </property>
-     <item row="1" column="2">
-      <widget class="ScrSpinBox" name="numberAlpha">
+     <item row="0" column="1">
+      <widget class="ColorSlider" name="sliderShade">
+       <property name="orientation">
+        <enum>Qt::Orientation::Horizontal</enum>
+       </property>
+       <property name="tickPosition">
+        <enum>QSlider::TickPosition::TicksAbove</enum>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="ColorSlider" name="sliderAlpha">
+       <property name="orientation">
+        <enum>Qt::Orientation::Horizontal</enum>
+       </property>
+       <property name="tickPosition">
+        <enum>QSlider::TickPosition::TicksAbove</enum>
+       </property>
+      </widget>
+     </item>
+     <item row="0" column="2">
+      <widget class="ScrSpinBox" name="numberShade">
        <property name="suffix">
         <string> %</string>
        </property>
@@ -59,28 +79,28 @@
        </property>
       </widget>
      </item>
-     <item row="1" column="1">
+     <item row="1" column="0">
       <widget class="QLabel" name="labelAlpha">
        <property name="text">
         <string>Opacity:</string>
        </property>
        <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
        </property>
       </widget>
      </item>
-     <item row="0" column="1">
+     <item row="0" column="0">
       <widget class="QLabel" name="labelShade">
        <property name="text">
         <string>Shade:</string>
        </property>
        <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
        </property>
       </widget>
      </item>
-     <item row="0" column="2">
-      <widget class="ScrSpinBox" name="numberShade">
+     <item row="1" column="2">
+      <widget class="ScrSpinBox" name="numberAlpha">
        <property name="suffix">
         <string> %</string>
        </property>
@@ -92,19 +112,6 @@
        </property>
       </widget>
      </item>
-     <item row="1" column="0">
-      <spacer name="horizontalSpacer">
-       <property name="orientation">
-        <enum>Qt::Horizontal</enum>
-       </property>
-       <property name="sizeHint" stdset="0">
-        <size>
-         <width>0</width>
-         <height>0</height>
-        </size>
-       </property>
-      </spacer>
-     </item>
     </layout>
    </item>
    <item>
@@ -136,6 +143,11 @@
    <header>ui/colorpicker/colorpicker_color_swatches.h</header>
    <container>1</container>
   </customwidget>
+  <customwidget>
+   <class>ColorSlider</class>
+   <extends>QSlider</extends>
+   <header>ui/widgets/color_slider.h</header>
+  </customwidget>
  </customwidgets>
  <tabstops>
   <tabstop>numberShade</tabstop>
Index: scribus/ui/dasheditor.cpp
===================================================================
--- scribus/ui/dasheditor.cpp	(Revision 27094)
+++ scribus/ui/dasheditor.cpp	(Arbeitskopie)
@@ -55,7 +55,6 @@
  *
  * ********************************************************************************* */
 
-const int RADIUS = 6;
 const double HAIRLINE = 0.001;
 
 DashPreview::DashPreview(QWidget *pa) : QFrame(pa)
Index: scribus/ui/factories/scribusproxystyle.cpp
===================================================================
--- scribus/ui/factories/scribusproxystyle.cpp	(Revision 27094)
+++ scribus/ui/factories/scribusproxystyle.cpp	(Arbeitskopie)
@@ -16,6 +16,8 @@
 #include "prefsmanager.h"
 #include "scribus.h"
 #include "ui/widgets/buttongroup.h"
+#include "ui/widgets/color_slider.h"
+#include "util_gui.h"
 
 // https://doc.qt.io/qt-6/qstyle.html#standardPalette
 QPalette createLightPalette()
@@ -32,7 +34,7 @@
 
 QPalette createDarkPalette()
 {
-	QPalette darkPalette/*(QColor(53, 53, 53))*/;
+	QPalette darkPalette;
 	darkPalette.setColor(QPalette::Window, QColor(53, 53, 53));
 	darkPalette.setColor(QPalette::WindowText, Qt::white);
 	darkPalette.setColor(QPalette::Base, QColor(42, 42, 42));
@@ -65,15 +67,17 @@
 {
 	QRect rect = QProxyStyle::subControlRect(cc, opt, sc, widget);
 
-	if (cc == CC_ToolButton)
+	switch (cc)
 	{
-		const ToolButton *tb = qobject_cast<const ToolButton*>(widget);
-		if (!tb)
+	case QStyle::ComplexControl::CC_ToolButton:
+	{
+		const ToolButton *wdg = qobject_cast<const ToolButton *>(widget);
+		if (!wdg)
 			return rect;
 
-		if (const QStyleOptionToolButton *button = qstyleoption_cast<const QStyleOptionToolButton *>(opt))
+		if (const QStyleOptionToolButton *styleOption = qstyleoption_cast<const QStyleOptionToolButton *>(opt))
 		{
-			switch (tb->position())
+			switch (wdg->position())
 			{
 			case ToolButton::Left:
 				rect.adjust(0, 0, rect.width(), 0);
@@ -89,30 +93,92 @@
 				break;
 			}
 
-			return visualRect(button->direction, button->rect, rect);
+			return visualRect(styleOption->direction, styleOption->rect, rect);
 		}
 	}
+	break;
+	case QStyle::ComplexControl::CC_Slider:
+	{
+		const ColorSlider *wdg = qobject_cast<const ColorSlider *>(widget);
+		if (!wdg)
+			return rect;
+
+		if (const QStyleOptionSlider *styleOption = qstyleoption_cast<const QStyleOptionSlider *>(opt))
+		{
+			QRect ret;
+			bool horizontal = styleOption->orientation == Qt::Horizontal;
+			int thickness = RADIUS * 2;
+			int center = (horizontal) ? (styleOption->rect.height() - thickness) / 2 : (styleOption->rect.width() - thickness) / 2;
+			int sliderOffset = (styleOption->tickPosition != QSlider::NoTicks) ? thickness : center;
+
+			switch (sc)
+			{
+			case SC_SliderHandle:
+			{
+				int len = thickness + 2; //proxy()->pixelMetric(PM_SliderLength, styleOption, widget);
+				int sliderPos = sliderPositionFromValue(styleOption->minimum, styleOption->maximum, styleOption->sliderPosition, (horizontal ? styleOption->rect.width() : styleOption->rect.height()) - len, styleOption->upsideDown);
+
+				if (horizontal)
+					ret.setRect(styleOption->rect.x() + sliderPos, styleOption->rect.y() + sliderOffset, len, thickness);
+				else
+					ret.setRect(styleOption->rect.x() + 1, styleOption->rect.y() + sliderPos, thickness, len);
+
+				break;
+			}
+			case SC_SliderGroove:
+				if (horizontal)
+					ret.setRect(styleOption->rect.x(), styleOption->rect.y() + sliderOffset + 1, styleOption->rect.width(), thickness - 3);
+				else
+					ret.setRect(styleOption->rect.x() + 2, styleOption->rect.y(), thickness - 3, styleOption->rect.height());
+
+				break;
+			case SC_SliderTickmarks:
+			{
+				int tickOffset = (styleOption->tickPosition != QSlider::NoTicks) ? thickness : 0;
+
+				if (styleOption->tickPosition != QSlider::NoTicks)
+				{
+					if (horizontal)
+						ret.setRect(styleOption->rect.x() + 1, styleOption->rect.y(), styleOption->rect.width() - 3, thickness);
+					else
+						ret.setRect(styleOption->rect.x() + tickOffset, styleOption->rect.y() + 1, thickness, styleOption->rect.height() - 3);
+				}
+				break;
+			}
+			default:
+				break;
+			}
+
+			return visualRect(styleOption->direction, styleOption->rect, ret);
+		}
+	}
+	break;
+	default:
+		break;
+	}
 
 	return rect;
 
 }
 
-void ScribusProxyStyle::drawControl(ControlElement el, const QStyleOption *opt, QPainter *painter, const QWidget *widget) const
+void ScribusProxyStyle::drawControl(ControlElement ce, const QStyleOption *opt, QPainter *painter, const QWidget *widget) const
 {
-	if (el == CE_ToolButtonLabel)
+	switch (ce)
+	{
+	case QStyle::ControlElement::CE_ToolButtonLabel:
 	{
 		QStyleOptionToolButton styleOption = *static_cast<const QStyleOptionToolButton *>(opt);
-		const ToolButton *tb = qobject_cast<const ToolButton*>(widget);
+		const ToolButton *wdg = qobject_cast<const ToolButton*>(widget);
 
-		if (!tb)
-			return QProxyStyle::drawControl(el, opt, painter, widget);
+		if (!wdg)
+			return QProxyStyle::drawControl(ce, opt, painter, widget);
 
 		QRect rect = styleOption.rect;
 		int t = widget->rect().top() + 4;
 		int b = widget->rect().bottom() - 4;
 		int r = widget->rect().right();
 
-		switch (tb->position())
+		switch (wdg->position())
 		{
 		case ToolButton::Right:
 			styleOption.rect.adjust(0, 0, rect.width() / 2, 0);
@@ -132,10 +198,113 @@
 			break;
 		}
 
-		return QProxyStyle::drawControl(el, &styleOption, painter, widget);
+		return QProxyStyle::drawControl(ce, &styleOption, painter, widget);
+	}
+	break;
+	default:
+		break;
 	}
 
-	return QProxyStyle::drawControl(el, opt, painter, widget);
+	return QProxyStyle::drawControl(ce, opt, painter, widget);
+}
+
+void ScribusProxyStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *painter, const QWidget *widget) const
+{
+	switch (cc)
+	{
+	case QStyle::ComplexControl::CC_Slider:
+	{
+		const ColorSlider *wdg = qobject_cast<const ColorSlider*>(widget);
+
+		if (!wdg)
+			return QProxyStyle::drawComplexControl(cc, opt, painter, widget);
+
+		if (const QStyleOptionSlider *styleOption = qstyleoption_cast<const QStyleOptionSlider *>(opt))
+		{
+			painter->save();
+			painter->setRenderHint(QPainter::Antialiasing, true);
+
+			QRect groove = subControlRect(CC_Slider, styleOption, SC_SliderGroove, widget);
+			QRect handle = subControlRect(CC_Slider, styleOption, SC_SliderHandle, widget);
+			QRect tickmarks = subControlRect(CC_Slider, styleOption, SC_SliderTickmarks, widget);
+
+			// Draw Slider
+			if ((styleOption->subControls & SC_SliderGroove) && groove.isValid())
+			{
+				double radius = (styleOption->orientation == Qt::Horizontal) ? groove.height() / 2 + 0.5 : groove.width() / 2 + 0.5;
+
+				QPainterPath border;
+				border.addRoundedRect(groove.toRectF().adjusted(0.5, 0.5, -0.5, -0.5), radius, radius);
+
+				// Draw Slider Gradients
+				painter->setClipPath(border);
+				if (wdg->m_background.isNull() || !widget->isEnabled())
+					painter->fillRect(groove, widget->palette().color(QPalette::Window));
+				else
+				{
+					renderCheckerPattern(painter, groove);
+					painter->drawImage(groove, wdg->m_background);
+				}
+				painter->setClipping(false);
+
+				// Draw Slider Border
+				painter->setPen(widget->palette().color(QPalette::Mid));
+				painter->setBrush(Qt::NoBrush);
+				painter->drawPath(border);
+				painter->setPen(Qt::NoPen);
+
+			}
+
+			// Draw Handle
+			if ((styleOption->subControls & SC_SliderHandle) && handle.isValid())
+			{
+				drawCircularHandle(painter, handle.center() + QPointF(0.5, 0.5), RADIUS * 2, Qt::NoBrush, widget->isEnabled());
+			}
+
+			// Draw Tickmarks
+			if ((styleOption->subControls & SC_SliderTickmarks) && tickmarks.isValid() && styleOption->tickInterval > 0)
+			{
+				QFont font = widget->font();
+				font.setPixelSize(8);
+				painter->setFont(font);
+
+				if (styleOption->orientation == Qt::Horizontal)
+				{
+					double ratio = double(tickmarks.width() - (2 * RADIUS)) / double(styleOption->tickInterval);
+
+					for (int x = 0; x <= styleOption->tickInterval; x++)
+					{
+						QRect tick(double(x) * ratio + RADIUS + 1, tickmarks.bottom() - 3, 1, 2);
+						QString label = QString::number((x * styleOption->maximum / styleOption->tickInterval) - abs(styleOption->minimum));
+						int offset = QFontMetrics(painter->font()).size(Qt::TextSingleLine, label).width() / 2;
+
+						painter->setPen(widget->palette().color(QPalette::WindowText));
+						painter->drawText(qBound(0, tick.left() - offset, tickmarks.width()), tickmarks.bottom() - 4, label);
+						painter->fillRect(tick, widget->palette().color(QPalette::WindowText));
+					}
+				}
+				else
+				{
+					double ratio = double(tickmarks.height() - (2 * RADIUS)) / double(styleOption->tickInterval);
+
+					for (int y = 0; y <= styleOption->tickInterval; y++)
+					{
+						QRect tick(tickmarks.left() + 2, double(y) * ratio + RADIUS + 1, 2, 1);
+						painter->fillRect(tick, widget->palette().color(QPalette::WindowText));
+					}
+				}
+			}
+
+			painter->restore();
+			return;
+		}
+	}
+	break;
+	default:
+		break;
+	}
+
+	QProxyStyle::drawComplexControl(cc, opt, painter, widget);
 }
 
 bool ScribusProxyStyle::eventFilter(QObject *object, QEvent *event)
Index: scribus/ui/factories/scribusproxystyle.h
===================================================================
--- scribus/ui/factories/scribusproxystyle.h	(Revision 27094)
+++ scribus/ui/factories/scribusproxystyle.h	(Arbeitskopie)
@@ -21,7 +21,9 @@
 	static ScribusProxyStyle* instance();
 
 	QRect subControlRect(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, QStyle::SubControl sc, const QWidget *widget = nullptr) const override;
-	void drawControl(QStyle::ControlElement el, const QStyleOption *opt, QPainter *painter, const QWidget *widget = nullptr) const override;
+	void drawControl(QStyle::ControlElement ce, const QStyleOption *opt, QPainter *painter, const QWidget *widget = nullptr) const override;
+	void drawComplexControl(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, QPainter *painter, const QWidget *widget) const override;
+
 
 protected:
 	bool eventFilter(QObject *object, QEvent *event) override;
Index: scribus/ui/gradientpreview.cpp
===================================================================
--- scribus/ui/gradientpreview.cpp	(Revision 27094)
+++ scribus/ui/gradientpreview.cpp	(Arbeitskopie)
@@ -29,7 +29,6 @@
 #include "gradientpreview.h"
 #include "util_gui.h"
 
-const int RADIUS = 6;
 const int GRADIENT_HEIGHT = 20;
 
 /* ********************************************************************************* *
Index: scribus/util_gui.h
===================================================================
--- scribus/util_gui.h	(Revision 27094)
+++ scribus/util_gui.h	(Arbeitskopie)
@@ -9,6 +9,8 @@
 class VColorStop;
 class ScPattern;
 
+const int RADIUS = 6;
+
 void SCRIBUS_API drawCircularHandle(QPainter * painter, QPointF center, qreal width, QBrush background, bool isEnabled = true);
 void SCRIBUS_API drawPointerHandle(QPainter * painter, QPointF pointer, qreal width, QColor background, bool isEnabled = true, bool isEmpty = false);
 void SCRIBUS_API renderCheckerPattern(QPainter *painter, QRectF rect);
colorslider_2025-10-25_01.patch (21,671 bytes)   
scribus.zip (5,099 bytes)
colorslider_mac.png (9,552 bytes)   
colorslider_mac.png (9,552 bytes)   

cbradney

2025-10-25 19:36

administrator   ~0053124

Works ok here, pending Jeans review of the other patch first
thanks

nitramr

2025-12-13 19:04

developer   ~0053370

Updated patch
colorslider_2025-12-13_01.diff (41,944 bytes)   
diff --git a/scribus/CMakeLists_Sources.txt b/scribus/CMakeLists_Sources.txt
index 8a71d0a99..afb5817be 100644
--- a/scribus/CMakeLists_Sources.txt
+++ b/scribus/CMakeLists_Sources.txt
@@ -567,6 +567,7 @@ set(SCRIBUS_SOURCES
 	ui/docks/dock_panelbase.cpp
 	ui/factories/scribusproxystyle.cpp
 	ui/widgets/color_button.cpp
+	ui/widgets/color_slider.cpp
 	ui/widgets/combo_blendmode.cpp
 	ui/widgets/combo_linestyle.cpp
 	ui/widgets/dropdown_button.cpp
diff --git a/scribus/ui/colorpicker/colorpicker_color.cpp b/scribus/ui/colorpicker/colorpicker_color.cpp
index 8e4d5c709..ac0907a1c 100644
--- a/scribus/ui/colorpicker/colorpicker_color.cpp
+++ b/scribus/ui/colorpicker/colorpicker_color.cpp
@@ -26,6 +26,9 @@ ColorPickerColor::ColorPickerColor(QWidget *parent) : QWidget(parent)
 
 	languageChange();
 
+	sliderAlpha->setMode(ColorSlider::Opacity);
+	sliderShade->setMode(ColorSlider::Shade);
+
 	connectSlots();
 	connect(sectionSwatches,    &SectionContainer::sizeChanged,				this, &ColorPickerColor::updateSize);
 }
@@ -34,14 +37,18 @@ void ColorPickerColor::connectSlots()
 {
 	connect(swatches,			&ColorPickerColorSwatches::colorChanged,	this, &ColorPickerColor::updateColorFromSwatches);	
 	connect(numberShade,		&ScrSpinBox::valueChanged,					this, &ColorPickerColor::updateColorShade);
+	connect(sliderShade,		&ColorSlider::valueChanged,					this, &ColorPickerColor::updateColorShade);
 	connect(numberAlpha,		&ScrSpinBox::valueChanged,					this, &ColorPickerColor::updateColorAlpha);
+	connect(sliderAlpha,		&ColorSlider::valueChanged,					this, &ColorPickerColor::updateColorAlpha);
 }
 
 void ColorPickerColor::disconnectSlots()
 {
 	disconnect(swatches,		&ColorPickerColorSwatches::colorChanged,	this, &ColorPickerColor::updateColorFromSwatches);
 	disconnect(numberShade,		&ScrSpinBox::valueChanged,					this, &ColorPickerColor::updateColorShade);
+	disconnect(sliderShade,		&ColorSlider::valueChanged,					this, &ColorPickerColor::updateColorShade);
 	disconnect(numberAlpha,		&ScrSpinBox::valueChanged,					this, &ColorPickerColor::updateColorAlpha);
+	disconnect(sliderAlpha,		&ColorSlider::valueChanged,					this, &ColorPickerColor::updateColorAlpha);
 }
 
 /* ********************************************************************************* *
@@ -73,8 +80,10 @@ void ColorPickerColor::setContext(Context config)
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(false);
 		labelShade->setVisible(false);
+		sliderShade->setVisible(false);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	case Context::Text:
 	case Context::TextBackground:
@@ -82,38 +91,48 @@ void ColorPickerColor::setContext(Context config)
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(true);
 		labelShade->setVisible(true);
+		sliderShade->setVisible(true);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	case Context::Fill:
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(true);
 		labelShade->setVisible(true);
+		sliderShade->setVisible(true);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	case Context::Line:
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(true);
 		labelShade->setVisible(true);
+		sliderShade->setVisible(true);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	case Context::FillMask:
 	case Context::LineMask:
 		sectionSwatches->setVisible(false);
 		numberShade->setVisible(false);
 		labelShade->setVisible(false);
+		sliderShade->setVisible(false);
 		numberAlpha->setVisible(true);
 		labelAlpha->setVisible(true);
+		sliderAlpha->setVisible(true);
 		isMask = true;
 		break;
 	case Context::DropShadow:
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(true);
 		labelShade->setVisible(true);
+		sliderShade->setVisible(true);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	}
 
@@ -183,23 +202,38 @@ QString ColorPickerColor::toolTipText() const
 void ColorPickerColor::setDoc(ScribusDoc *doc)
 {
 	m_doc = doc;
+	sliderAlpha->setDoc(m_doc);
+	sliderShade->setDoc(m_doc);
 }
 
 void ColorPickerColor::setColorData(const CPColorData& color)
 {
+	if (!m_doc)
+		return;
+
 	m_color = color;
 
+	bool hasColor = m_color.Name != CommonStrings::tr_NoneColor;
+
 	// update UI + controls
 	QSignalBlocker sigSwatches(swatches);
 	swatches->setCurrentColor(m_color.Name);
 
 	QSignalBlocker sigShade(numberShade);
 	numberShade->setValue(m_color.Shade);
-	numberShade->setEnabled(m_color.Name != CommonStrings::tr_NoneColor);
+	numberShade->setEnabled(hasColor);
 
 	QSignalBlocker sigAlpha(numberAlpha);
 	numberAlpha->setValue(qRound(100 - (m_color.Opacity * 100)));
-	numberAlpha->setEnabled(m_color.Name != CommonStrings::tr_NoneColor);
+	numberAlpha->setEnabled(hasColor);
+
+	QSignalBlocker sigAlphaSlider(sliderAlpha);
+	sliderAlpha->setEnabled(hasColor);
+	sliderAlpha->setValues(m_color.Name, numberAlpha->value(), numberShade->value());
+
+	QSignalBlocker sigShadeSlider(sliderShade);
+	sliderShade->setEnabled(hasColor);
+	sliderShade->setValues(m_color.Name, numberAlpha->value(), numberShade->value());
 
 }
 
@@ -211,20 +245,50 @@ void ColorPickerColor::languageChange()
 void ColorPickerColor::updateColorFromSwatches()
 {
 	m_color.Name = swatches->currentColor();
-	numberShade->setEnabled(m_color.Name != CommonStrings::tr_NoneColor);
-	numberAlpha->setEnabled(m_color.Name != CommonStrings::tr_NoneColor);
+	bool hasColor = m_color.Name != CommonStrings::tr_NoneColor;
+
+	numberShade->setEnabled(hasColor);
+	sliderShade->setEnabled(hasColor);
+	sliderShade->setValues(m_color.Name, numberAlpha->value(), numberShade->value());
+	numberAlpha->setEnabled(hasColor);
+	sliderAlpha->setEnabled(hasColor);
+	sliderAlpha->setValues(m_color.Name, numberAlpha->value(), numberShade->value());
 
 	updateColor();
 }
 
 void ColorPickerColor::updateColorShade()
 {
+	QObject* obj = sender();
+	if( obj == sliderShade )
+	{
+		QSignalBlocker sigBlocker(numberShade);
+		numberShade->setValue(sliderShade->value());
+	}
+	else if( obj == numberShade )
+	{
+		QSignalBlocker sigBlocker(sliderShade);
+		sliderShade->setValue(numberShade->value());
+	}
+
 	m_color.Shade = numberShade->value();
 	updateColor();
 }
 
 void ColorPickerColor::updateColorAlpha()
 {
+	QObject* obj = sender();
+	if( obj == sliderAlpha )
+	{
+		QSignalBlocker sigBlocker(numberAlpha);
+		numberAlpha->setValue(sliderAlpha->value());
+	}
+	else if( obj == numberAlpha )
+	{
+		QSignalBlocker sigBlocker(sliderAlpha);
+		sliderAlpha->setValue(numberAlpha->value());
+	}
+
 	m_color.Opacity = (100 - numberAlpha->value()) / 100.0;
 	updateColor();
 }
diff --git a/scribus/ui/colorpicker/colorpicker_color.ui b/scribus/ui/colorpicker/colorpicker_color.ui
index 4ee50c4ed..96550bf2f 100644
--- a/scribus/ui/colorpicker/colorpicker_color.ui
+++ b/scribus/ui/colorpicker/colorpicker_color.ui
@@ -59,51 +59,58 @@
        </property>
       </widget>
      </item>
-     <item row="1" column="1">
-      <widget class="QLabel" name="labelAlpha">
-       <property name="text">
-        <string>Opacity:</string>
+     <item row="0" column="2">
+      <widget class="ScrSpinBox" name="numberShade">
+       <property name="suffix">
+        <string> %</string>
        </property>
-       <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       <property name="maximum">
+        <double>100.000000000000000</double>
+       </property>
+       <property name="singleStep">
+        <double>10.000000000000000</double>
        </property>
       </widget>
      </item>
-     <item row="0" column="1">
+     <item row="0" column="0">
       <widget class="QLabel" name="labelShade">
        <property name="text">
         <string>Shade:</string>
        </property>
        <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
        </property>
       </widget>
      </item>
-     <item row="0" column="2">
-      <widget class="ScrSpinBox" name="numberShade">
-       <property name="suffix">
-        <string> %</string>
-       </property>
-       <property name="maximum">
-        <double>100.000000000000000</double>
+     <item row="0" column="1">
+      <widget class="ColorSlider" name="sliderShade">
+       <property name="orientation">
+        <enum>Qt::Orientation::Horizontal</enum>
        </property>
-       <property name="singleStep">
-        <double>10.000000000000000</double>
+       <property name="tickPosition">
+        <enum>QSlider::TickPosition::TicksAbove</enum>
        </property>
       </widget>
      </item>
      <item row="1" column="0">
-      <spacer name="horizontalSpacer">
+      <widget class="QLabel" name="labelAlpha">
+       <property name="text">
+        <string>Opacity:</string>
+       </property>
+       <property name="alignment">
+        <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
+       </property>
+      </widget>
+     </item>
+     <item row="1" column="1">
+      <widget class="ColorSlider" name="sliderAlpha">
        <property name="orientation">
-        <enum>Qt::Horizontal</enum>
+        <enum>Qt::Orientation::Horizontal</enum>
        </property>
-       <property name="sizeHint" stdset="0">
-        <size>
-         <width>0</width>
-         <height>0</height>
-        </size>
+       <property name="tickPosition">
+        <enum>QSlider::TickPosition::TicksAbove</enum>
        </property>
-      </spacer>
+      </widget>
      </item>
     </layout>
    </item>
@@ -136,6 +143,11 @@
    <header>ui/colorpicker/colorpicker_color_swatches.h</header>
    <container>1</container>
   </customwidget>
+  <customwidget>
+   <class>ColorSlider</class>
+   <extends>QSlider</extends>
+   <header>ui/widgets/color_slider.h</header>
+  </customwidget>
  </customwidgets>
  <tabstops>
   <tabstop>numberShade</tabstop>
diff --git a/scribus/ui/dasheditor.cpp b/scribus/ui/dasheditor.cpp
index c0cfbf77b..6d6d3d358 100644
--- a/scribus/ui/dasheditor.cpp
+++ b/scribus/ui/dasheditor.cpp
@@ -55,7 +55,6 @@ for which a new license (GPL+exception) is in place.
  *
  * ********************************************************************************* */
 
-const int RADIUS = 6;
 const double HAIRLINE = 0.001;
 
 DashPreview::DashPreview(QWidget *pa) : QFrame(pa)
diff --git a/scribus/ui/factories/scribusproxystyle.cpp b/scribus/ui/factories/scribusproxystyle.cpp
index 5295b27a4..94ce7dbde 100644
--- a/scribus/ui/factories/scribusproxystyle.cpp
+++ b/scribus/ui/factories/scribusproxystyle.cpp
@@ -18,6 +18,8 @@
 
 #include "prefsmanager.h"
 #include "scribus.h"
+#include "ui/widgets/color_slider.h"
+#include "util_gui.h"
 
 // https://doc.qt.io/qt-6/qstyle.html#standardPalette
 QPalette createLightPalette()
@@ -64,6 +66,167 @@ ScribusProxyStyle* ScribusProxyStyle::instance()
 	return static_cast<ScribusProxyStyle*>(qApp->style());
 }
 
+QRect ScribusProxyStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *option, SubControl sc, const QWidget *widget) const
+{
+	QRect rect = QProxyStyle::subControlRect(cc, option, sc, widget);
+
+	switch (cc)
+	{
+	case QStyle::ComplexControl::CC_Slider:
+	{
+		const QStyleOptionSlider *soSlider = qstyleoption_cast<const QStyleOptionSlider *>(option);
+
+		if (soSlider && widget->inherits("ColorSlider") )
+		{
+			QRect ret;
+			bool horizontal = soSlider->orientation == Qt::Horizontal;
+			int thickness = RADIUS * 2;
+			int center = (horizontal) ? (soSlider->rect.height() - thickness) / 2 : (soSlider->rect.width() - thickness) / 2;
+			int sliderOffset = (soSlider->tickPosition != QSlider::NoTicks) ? thickness : center;
+
+			switch (sc)
+			{
+			case SC_SliderHandle:
+			{
+				int len = thickness + 2; //proxy()->pixelMetric(PM_SliderLength, styleOption, widget);
+				int sliderPos = sliderPositionFromValue(soSlider->minimum, soSlider->maximum, soSlider->sliderPosition, (horizontal ? soSlider->rect.width() : soSlider->rect.height()) - len, soSlider->upsideDown);
+
+				if (horizontal)
+					ret.setRect(soSlider->rect.x() + sliderPos, soSlider->rect.y() + sliderOffset, len, thickness);
+				else
+					ret.setRect(soSlider->rect.x() + 1, soSlider->rect.y() + sliderPos, thickness, len);
+
+				break;
+			}
+			case SC_SliderGroove:
+				if (horizontal)
+					ret.setRect(soSlider->rect.x(), soSlider->rect.y() + sliderOffset + 1, soSlider->rect.width(), thickness - 3);
+				else
+					ret.setRect(soSlider->rect.x() + 2, soSlider->rect.y(), thickness - 3, soSlider->rect.height());
+
+				break;
+			case SC_SliderTickmarks:
+			{
+				int tickOffset = (soSlider->tickPosition != QSlider::NoTicks) ? thickness : 0;
+
+				if (soSlider->tickPosition != QSlider::NoTicks)
+				{
+					if (horizontal)
+						ret.setRect(soSlider->rect.x() + 1, soSlider->rect.y(), soSlider->rect.width() - 3, thickness);
+					else
+						ret.setRect(soSlider->rect.x() + tickOffset, soSlider->rect.y() + 1, thickness, soSlider->rect.height() - 3);
+				}
+				break;
+			}
+			default:
+				break;
+			}
+
+			return visualRect(soSlider->direction, soSlider->rect, ret);
+		}
+	}
+	break;
+	default:
+		break;
+	}
+
+	return rect;
+}
+
+void ScribusProxyStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *painter, const QWidget *widget) const
+{
+	switch (cc)
+	{
+	case QStyle::ComplexControl::CC_Slider:
+	{
+		const ColorSlider *wdg = qobject_cast<const ColorSlider *>(widget);
+		const QStyleOptionSlider *soSlider = qstyleoption_cast<const QStyleOptionSlider *>(opt);
+
+		if (soSlider && wdg)
+		{
+			painter->save();
+			painter->setRenderHint(QPainter::Antialiasing, true);
+
+			QRect groove = subControlRect(CC_Slider, soSlider, SC_SliderGroove, widget);
+			QRect handle = subControlRect(CC_Slider, soSlider, SC_SliderHandle, widget);
+			QRect tickmarks = subControlRect(CC_Slider, soSlider, SC_SliderTickmarks, widget);
+
+			// Draw Slider
+			if ((soSlider->subControls & SC_SliderGroove) && groove.isValid())
+			{
+				double radius = (soSlider->orientation == Qt::Horizontal) ? groove.height() / 2 + 0.5 : groove.width() / 2 + 0.5;
+
+				QPainterPath border;
+				border.addRoundedRect(groove.toRectF().adjusted(0.5, 0.5, -0.5, -0.5), radius, radius);
+
+				// Draw Slider Gradients
+				painter->setClipPath(border);
+				if (wdg->m_background.isNull() || !widget->isEnabled())
+					painter->fillRect(groove, widget->palette().color(QPalette::Window));
+				else
+				{
+					renderCheckerPattern(painter, groove);
+					painter->drawImage(groove, wdg->m_background);
+				}
+				painter->setClipping(false);
+
+				// Draw Slider Border
+				painter->setPen(widget->palette().color(QPalette::Mid));
+				painter->setBrush(Qt::NoBrush);
+				painter->drawPath(border);
+				painter->setPen(Qt::NoPen);
+			}
+
+			// Draw Handle
+			if ((soSlider->subControls & SC_SliderHandle) && handle.isValid())
+				drawCircularHandle(painter, handle.center() + QPointF(0.5, 0.5), RADIUS * 2, Qt::NoBrush, widget->isEnabled());
+
+			// Draw Tickmarks
+			if ((soSlider->subControls & SC_SliderTickmarks) && tickmarks.isValid() && soSlider->tickInterval > 0)
+			{
+				QFont font = widget->font();
+				font.setPixelSize(8);
+				painter->setFont(font);
+
+				if (soSlider->orientation == Qt::Horizontal)
+				{
+					double ratio = double(tickmarks.width() - (2 * RADIUS)) / double(soSlider->tickInterval);
+
+					for (int x = 0; x <= soSlider->tickInterval; x++)
+					{
+						QRect tick(double(x) * ratio + RADIUS + 1, tickmarks.bottom() - 3, 1, 2);
+						QString label = QString::number((x * soSlider->maximum / soSlider->tickInterval) - abs(soSlider->minimum));
+						int offset = QFontMetrics(painter->font()).size(Qt::TextSingleLine, label).width() / 2;
+
+						painter->setPen(widget->palette().color(QPalette::WindowText));
+						painter->drawText(qBound(0, tick.left() - offset, tickmarks.width()), tickmarks.bottom() - 4, label);
+						painter->fillRect(tick, widget->palette().color(QPalette::WindowText));
+					}
+				}
+				else
+				{
+					double ratio = double(tickmarks.height() - (2 * RADIUS)) / double(soSlider->tickInterval);
+
+					for (int y = 0; y <= soSlider->tickInterval; y++)
+					{
+						QRect tick(tickmarks.left() + 2, double(y) * ratio + RADIUS + 1, 2, 1);
+						painter->fillRect(tick, widget->palette().color(QPalette::WindowText));
+					}
+				}
+			}
+
+			painter->restore();
+			return;
+		}
+	}
+		break;
+	default:
+		break;
+	}
+
+	QProxyStyle::drawComplexControl(cc, opt, painter, widget);
+}
+
 bool ScribusProxyStyle::eventFilter(QObject *object, QEvent *event)
 {
 	if(object == qApp && event->type() == QEvent::ThemeChange)
diff --git a/scribus/ui/factories/scribusproxystyle.h b/scribus/ui/factories/scribusproxystyle.h
index 6924af8d2..308d8594b 100644
--- a/scribus/ui/factories/scribusproxystyle.h
+++ b/scribus/ui/factories/scribusproxystyle.h
@@ -20,6 +20,9 @@ public:
 
 	static ScribusProxyStyle* instance();
 
+	QRect subControlRect(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, QStyle::SubControl sc, const QWidget *widget = nullptr) const override;
+	void drawComplexControl(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, QPainter *painter, const QWidget *widget) const override;
+
 protected:
 	bool eventFilter(QObject *object, QEvent *event) override;
 
diff --git a/scribus/ui/gradientpreview.cpp b/scribus/ui/gradientpreview.cpp
index b3684304c..191276a2d 100644
--- a/scribus/ui/gradientpreview.cpp
+++ b/scribus/ui/gradientpreview.cpp
@@ -29,7 +29,6 @@ for which a new license (GPL+exception) is in place.
 #include "gradientpreview.h"
 #include "util_gui.h"
 
-const int RADIUS = 6;
 const int GRADIENT_HEIGHT = 20;
 
 /* ********************************************************************************* *
diff --git a/scribus/ui/widgets/color_slider.cpp b/scribus/ui/widgets/color_slider.cpp
new file mode 100644
index 000000000..2d45d1bd7
--- /dev/null
+++ b/scribus/ui/widgets/color_slider.cpp
@@ -0,0 +1,721 @@
+#include "color_slider.h"
+
+#include <QPaintEvent>
+#include <QPainter>
+#include <QPainterPath>
+#include <QPalette>
+#include <QStyle>
+#include <QStyleOption>
+#include <QWidget>
+
+#include "sccolorengine.h"
+#include "scribusdoc.h"
+#include "util_gui.h"
+
+/* ********************************************************************************* *
+ *
+ * Constructor + Setup
+ *
+ * ********************************************************************************* */
+
+ColorSlider::ColorSlider(QWidget *parent): ColorSlider(Mode::RGBRed, parent) {}
+
+ColorSlider::ColorSlider(Mode mode, QWidget *parent): QSlider(parent)
+{
+	m_mode = mode;
+	m_color = ScColor(255, 0, 0);
+
+	setMinimumSize(24, 24);
+	setOrientation(Qt::Horizontal);
+	setup();
+	updateValues();
+}
+
+void ColorSlider::setDoc(ScribusDoc *doc)
+{
+	m_doc = doc;
+}
+
+/* ********************************************************************************* *
+ *
+ * Slots
+ *
+ * ********************************************************************************* */
+
+void ColorSlider::setMode(Mode mode)
+{
+	m_mode = mode;
+	setup();
+	updateValues();
+	renderMap();
+	update();
+}
+
+void ColorSlider::setColor(const QString &colorName)
+{
+	if (!m_doc)
+		return;
+
+	if (colorName == CommonStrings::tr_NoneColor || colorName == CommonStrings::None)
+	{
+		m_background = QImage(width(), height(), QImage::Format_ARGB32); // if there is no image we draw the window color
+		m_background.fill(Qt::transparent);
+	}
+	else if (colorName.isEmpty())
+		setColor(m_doc->PageColors.value("White"));
+	else
+		setColor(m_doc->PageColors.value(colorName));
+}
+
+void ColorSlider::setColor(ScColor color)
+{
+	m_color = color;
+	updateValues();
+	renderMap();
+	update();
+}
+
+void ColorSlider::setShade(double shade)
+{
+	m_shade = shade;
+	updateValues();
+	update();
+}
+
+void ColorSlider::setOpacity(double opacity)
+{
+	m_opacity = opacity;
+	updateValues();
+	update();
+}
+
+void ColorSlider::setIsDynamic(double dynamic)
+{
+	m_dynamicColor = dynamic;
+	renderMap();
+	update();
+}
+
+void ColorSlider::setValues(const QString &colorName, double opacity, double shade)
+{
+	if (!m_doc)
+		return;
+
+	if (colorName == CommonStrings::tr_NoneColor || colorName == CommonStrings::None)
+	{
+		m_background = QImage(width(), height(), QImage::Format_ARGB32); // if there is no image we draw the window color
+		m_background.fill(Qt::transparent);
+	}
+	else if (!colorName.isEmpty() || m_doc->PageColors.contains(colorName))
+		setValues(m_doc->PageColors.value(colorName), opacity, shade);
+	else
+		setValues(m_doc->PageColors.value("White"), opacity, shade);
+}
+
+void ColorSlider::setValues(ScColor color, double opacity, double shade)
+{
+	m_color = color;
+	m_shade = shade;
+	m_opacity = opacity;
+	updateValues();
+	renderMap();
+	update();
+}
+
+/* ********************************************************************************* *
+ *
+ * Functions
+ *
+ * ********************************************************************************* */
+
+void ColorSlider::updateColor()
+{
+	if (!m_doc)
+		return;
+
+	double v1, v2, v3, v4;
+
+	switch (m_mode)
+	{
+	case Mode::Opacity:
+		m_opacity = value();
+		break;
+	case Mode::Shade:
+		m_shade = value();
+		break;
+	case Mode::CMYKCyan:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelCMYK);
+		m_color.getCMYK(&v1, &v2, &v3, &v4);
+		m_color.setCmykColorF(value() / (double) maximum(), v2, v3, v4);
+		break;
+	case Mode::CMYKMagenta:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelCMYK);
+		m_color.getCMYK(&v1, &v2, &v3, &v4);
+		m_color.setCmykColorF(v1, value() / (double) maximum(), v3, v4);
+		break;
+	case Mode::CMYKYellow:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelCMYK);
+		m_color.getCMYK(&v1, &v2, &v3, &v4);
+		m_color.setCmykColorF(v1, v2, value() / (double) maximum(), v4);
+		break;
+	case Mode::CMYKKey:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelCMYK);
+		m_color.getCMYK(&v1, &v2, &v3, &v4);
+		m_color.setCmykColorF(v1, v2, v3, value() / (double) maximum());
+		break;
+	case Mode::HSVHue:
+	{
+		QColor color = ScColorEngine::getRGBColor(m_color, m_doc);
+		color.setHslF(value() / (double) maximum(), color.saturationF(), color.valueF());
+		m_color.fromQColor(color);
+		break;
+	}
+	case Mode::HSVSaturation:
+	{
+		QColor color = ScColorEngine::getRGBColor(m_color, m_doc);
+		color.setHslF(color.hslHueF(), value() / (double) maximum(), color.valueF());
+		m_color.fromQColor(color);
+		break;
+	}
+	case Mode::HSVValue:
+	{
+		QColor color = ScColorEngine::getRGBColor(m_color, m_doc);
+		color.setHslF(color.hslHueF(), color.saturationF(), value() / (double) maximum());
+		m_color.fromQColor(color);
+		break;
+	}
+	case Mode::HLCHue:
+	{
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelLab);
+		m_color.getLab(&v1, &v2, &v3);
+		QLineF lin(QPointF(0.0, 0.0), QPointF(v2, v3));
+		lin = QLineF::fromPolar(lin.length(), 360 - value());
+		m_color.setLabColor(v1, lin.p2().x(), lin.p2().y());
+		break;
+	}
+	case Mode::HLCChroma:
+	{
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelLab);
+		m_color.getLab(&v1, &v2, &v3);
+		QLineF lin(QPointF(0.0, 0.0), QPointF(v2, v3));
+		lin = QLineF::fromPolar(value(), lin.angle());
+		m_color.setLabColor(v1, lin.p2().x(), lin.p2().y());
+		break;
+	}
+	case Mode::HLCLightness:
+	case Mode::LabLightness:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelLab);
+		m_color.getLab(&v1, &v2, &v3);
+		m_color.setLabColor(value(), v2, v3);
+		break;
+	case Mode::LabA:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelLab);
+		m_color.getLab(&v1, &v2, &v3);
+		m_color.setLabColor(v1, value(), v3);
+		break;
+	case Mode::LabB:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelLab);
+		m_color.getLab(&v1, &v2, &v3);
+		m_color.setLabColor(v1, v2, value());
+		break;
+	case Mode::RGBRed:
+	case Mode::RGBWebRed:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelRGB);
+		m_color.getRGB(&v1, &v2, &v3);
+		m_color.setRgbColorF(QSlider::value() / (double) maximum(), v2, v3);
+		break;
+	case Mode::RGBGreen:
+	case Mode::RGBWebGreen:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelRGB);
+		m_color.getRGB(&v1, &v2, &v3);
+		m_color.setRgbColorF(v1, QSlider::value() / (double) maximum(), v3);
+		break;
+	case Mode::RGBBlue:
+	case Mode::RGBWebBlue:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelRGB);
+		m_color.getRGB(&v1, &v2, &v3);
+		m_color.setRgbColorF(v1, v2, QSlider::value() / (double) maximum());
+		break;
+	}
+
+	emit colorChanged(m_color);
+}
+
+void ColorSlider::setup()
+{
+	disconnectSlots();
+
+	switch (m_mode)
+	{
+	default:
+	case Mode::Opacity:
+	case Mode::CMYKCyan:
+	case Mode::CMYKMagenta:
+	case Mode::CMYKYellow:
+	case Mode::CMYKKey:
+	case Mode::HSVSaturation:
+	case Mode::HSVValue:
+	case Mode::HLCLightness:
+	case Mode::LabLightness:
+	case Mode::Shade:
+		setMinimum(0);
+		setMaximum(100);
+		setTickInterval(4);
+		m_colorSteps = 100;
+		break;
+	case Mode::HSVHue:
+	case Mode::HLCHue:
+		setMinimum(0);
+		setMaximum(359);
+		setTickInterval(6);
+		m_colorSteps = 359;
+		break;
+	case Mode::LabA:
+	case Mode::LabB:
+		setMinimum(-128);
+		setMaximum(128);
+		setTickInterval(4);
+		m_colorSteps = 256;
+		break;
+	case Mode::HLCChroma:
+		setMinimum(0);
+		setMaximum(128);
+		setTickInterval(4);
+		m_colorSteps = 128;
+		break;
+	case Mode::RGBRed:
+	case Mode::RGBGreen:
+	case Mode::RGBBlue:
+		setMinimum(0);
+		setMaximum(255);
+		setTickInterval(2);
+		m_colorSteps = 255;
+		break;
+	case Mode::RGBWebRed:
+	case Mode::RGBWebGreen:
+	case Mode::RGBWebBlue:
+		setMinimum(0);
+		setMaximum(255);
+		setTickInterval(5);
+		m_colorSteps = 5;
+		break;
+	}
+
+	setSingleStep(maximum() / m_colorSteps);
+	setPageStep(maximum() / m_colorSteps);
+
+	connectSlots();
+}
+
+void ColorSlider::connectSlots()
+{
+	connect(this, &QSlider::valueChanged, this, &ColorSlider::updateColor);
+}
+
+void ColorSlider::disconnectSlots()
+{
+	disconnect(this, &QSlider::valueChanged, this, &ColorSlider::updateColor);
+}
+
+void ColorSlider::renderMap()
+{
+	if (!m_doc || !isVisible())
+		return;
+
+	QRect rect;
+	QPoint pEnd;
+	QColor color = ScColorEngine::getDisplayColor(m_color, m_doc);
+
+	if (orientation() == Qt::Horizontal)
+	{
+		rect = QRect(0, 0, width(), 1);
+		pEnd = rect.topRight();
+	}
+	else
+	{
+		rect = QRect(0, 0, 1, height());
+		pEnd = rect.bottomLeft();
+	}
+
+	m_background = QImage(rect.width(), rect.height(), QImage::Format_ARGB32);
+	m_background.fill(Qt::transparent);
+
+	QPainter p(&m_background);
+	p.setPen(Qt::NoPen);
+
+	// For opacity and shade we can use simple linear gradients
+	if (m_mode == Mode::Opacity || m_mode == Mode::Shade)
+	{
+		QLinearGradient grad = QLinearGradient(QPoint(0, 0), pEnd);
+
+		switch (m_mode)
+		{
+		case Mode::Opacity:
+		{
+			QColor colorA = color;
+			colorA.setAlphaF(0);
+			grad.setColorAt(0, colorA);
+			grad.setColorAt(1, color);
+			p.setBrush(grad);
+			p.drawRect(m_background.rect());
+			break;
+		}
+		default:
+		case Mode::Shade:
+		{
+			grad.setColorAt(0, Qt::white);
+			grad.setColorAt(1, color);
+			p.setBrush(grad);
+			p.drawRect(m_background.rect());
+			break;
+		}
+		}
+	}
+	else
+	{
+		double l, a, b;
+		int c, m, y, k, red, green, blue;
+		int length = qMax(rect.width(), rect.height());
+		int step = qMax(1, (length - RADIUS * 2) / m_colorSteps); // it must be at leat 1 (px)
+
+		// CMYK
+		CMYKColor cmyk;
+		ScColorEngine::getCMYKValues(m_color, m_doc, cmyk);
+		cmyk.getValues(c, m, y, k);
+
+		// RGB
+		RGBColor rgb;
+		ScColorEngine::getRGBValues(m_color, m_doc, rgb);
+		rgb.getValues(red, green, blue);
+
+		// Lab / HLC
+		ScColor sColor = ScColorEngine::convertToModel(m_color, m_doc, colorModelLab);
+		sColor.getLab(&l, &a, &b);
+		QLineF lin(QPointF(0.0, 0.0), QPointF(a, b));
+		double H = lin.angle();
+		double C = lin.length();
+
+		for (int x = 0; x <= length; x += step)
+		{
+			switch (m_mode)
+			{
+			default:
+			case Mode::CMYKCyan:
+			{
+				int val = qRound(((orientation() == Qt::Horizontal) ? qreal(x) / length : 1. - qreal(x) / length) * 255);
+				if (isDynamic())
+					color = ScColorEngine::getDisplayColorGC(ScColor(val, m, y, k), m_doc);
+				else
+					color = ScColorEngine::getDisplayColorGC(ScColor(val, 0, 0, 0), m_doc);
+			}
+			break;
+			case Mode::CMYKMagenta:
+			{
+				int val = qRound(((orientation() == Qt::Horizontal) ? qreal(x) / length : 1. - qreal(x) / length) * 255);
+				if (isDynamic())
+					color = ScColorEngine::getDisplayColorGC(ScColor(c, val, y, k), m_doc);
+				else
+					color = ScColorEngine::getDisplayColorGC(ScColor(0, val, 0, 0), m_doc);
+			}
+			break;
+			case Mode::CMYKYellow:
+			{
+				int val = qRound(((orientation() == Qt::Horizontal) ? qreal(x) / length : 1. - qreal(x) / length) * 255);
+				if (isDynamic())
+					color = ScColorEngine::getDisplayColorGC(ScColor(c, m, val, k), m_doc);
+				else
+					color = ScColorEngine::getDisplayColorGC(ScColor(0, 0, val, 0), m_doc);
+			}
+			break;
+			case Mode::CMYKKey:
+			{
+				int val = qRound(((orientation() == Qt::Horizontal) ? qreal(x) / length : 1. - qreal(x) / length) * 255);
+				if (isDynamic())
+					color = ScColorEngine::getDisplayColorGC(ScColor(c, m, y, val), m_doc);
+				else
+					color = ScColorEngine::getDisplayColorGC(ScColor(0, 0, 0, val), m_doc);
+			}
+			break;
+			case Mode::HLCHue:
+			{
+				qreal val = (orientation() == Qt::Horizontal) ? qreal(x) / length : 1. - qreal(x) / length;
+				lin = QLineF::fromPolar(C, -360 * val);
+				if (isDynamic())
+					color = ScColorEngine::getDisplayColorGC(ScColor(l, lin.p2().x(), lin.p2().y()), m_doc);
+				else
+					color = ScColorEngine::getDisplayColorGC(ScColor(100.0, lin.p2().x(), lin.p2().y()), m_doc);
+			}
+			break;
+			case Mode::HLCChroma:
+			{
+				qreal val = (orientation() == Qt::Horizontal) ? qreal(x) / length : 1. - qreal(x) / length;
+				lin = QLineF::fromPolar(128 * val, H);
+				if (isDynamic())
+					color = ScColorEngine::getDisplayColorGC(ScColor(l, lin.p2().x(), lin.p2().y()), m_doc);
+				else
+					color = ScColorEngine::getDisplayColorGC(ScColor(100.0, lin.p2().x(), lin.p2().y()), m_doc);
+			}
+			break;
+			case Mode::HLCLightness:
+			case Mode::LabLightness:
+			{
+				qreal val = ((orientation() == Qt::Horizontal) ? qreal(x) / length : 1. - qreal(x) / length) * 100.0;
+				if (isDynamic())
+					color = ScColorEngine::getDisplayColorGC(ScColor(val, a, b), m_doc);
+				else
+					color = ScColorEngine::getDisplayColorGC(ScColor(val, 0.0, 0.0), m_doc);
+			}
+			break;
+			case Mode::LabA:
+			{
+				qreal val = ((orientation() == Qt::Horizontal) ? -(0.5 - qreal(x) / length) : (0.5 - qreal(x) / length)) * 255.;
+				if (isDynamic())
+					color = ScColorEngine::getDisplayColorGC(ScColor(l, val, b), m_doc);
+				else
+					color = ScColorEngine::getDisplayColorGC(ScColor(0.0, val, 0.0), m_doc);
+			}
+			break;
+			case Mode::LabB:
+			{
+				qreal val = ((orientation() == Qt::Horizontal) ? -(0.5 - qreal(x) / length) : (0.5 - qreal(x) / length)) * 255.;
+				if (isDynamic())
+					color = ScColorEngine::getDisplayColorGC(ScColor(l, a, val), m_doc);
+				else
+					color = ScColorEngine::getDisplayColorGC(ScColor(0.0, 0.0, val), m_doc);
+			}
+			break;
+			case Mode::RGBRed:
+			case Mode::RGBWebRed:
+			{
+				int val = qRound(((orientation() == Qt::Horizontal) ? qreal(x) / length : 1. - qreal(x) / length) * 255);
+				if (isDynamic())
+					color = ScColorEngine::getDisplayColorGC(ScColor(val, green, blue), m_doc);
+				else
+					color = ScColorEngine::getDisplayColorGC(ScColor(val, 0, 0), m_doc);
+			}
+			break;
+			case Mode::RGBGreen:
+			case Mode::RGBWebGreen:
+			{
+				int val = qRound(((orientation() == Qt::Horizontal) ? qreal(x) / length : 1. - qreal(x) / length) * 255);
+				if (isDynamic())
+					color = ScColorEngine::getDisplayColorGC(ScColor(red, val, blue), m_doc);
+				else
+					color = ScColorEngine::getDisplayColorGC(ScColor(0, val, 0), m_doc);
+			}
+			break;
+			case Mode::RGBBlue:
+			case Mode::RGBWebBlue:
+			{
+				int val = qRound(((orientation() == Qt::Horizontal) ? qreal(x) / length : 1. - qreal(x) / length) * 255);
+				if (isDynamic())
+					color = ScColorEngine::getDisplayColorGC(ScColor(red, green, val), m_doc);
+				else
+					color = ScColorEngine::getDisplayColorGC(ScColor(0, 0, val), m_doc);
+			}
+			break;
+			case Mode::HSVHue:
+			{
+				qreal val = (orientation() == Qt::Horizontal) ? qreal(x) / length : 1. - qreal(x) / length;
+				if (isDynamic())
+					color.setHsvF(val, color.saturationF(), color.valueF());
+				else
+					color.setHsvF(val, 1.0, 0.5);
+			}
+			break;
+			case Mode::HSVSaturation:
+			{
+				qreal val = (orientation() == Qt::Horizontal) ? qreal(x) / length : 1. - qreal(x) / length;
+				if (isDynamic())
+					color.setHsvF(color.hueF(), val, color.valueF());
+				else
+					color.setHsvF(0.0, val, 0.5);
+			}
+			break;
+			case Mode::HSVValue:
+			{
+				qreal val = (orientation() == Qt::Horizontal) ? qreal(x) / length : 1. - qreal(x) / length;
+				if (isDynamic())
+					color.setHsvF(color.hueF(), color.saturationF(), val);
+				else
+					color.setHsvF(0.0, 1.0, val);
+			}
+			break;
+			}
+
+			int w = (x == 0) ? step + RADIUS : step;
+			int o = (x == 0) ? 0 : RADIUS;
+
+			p.setBrush(color);
+			p.drawRect((orientation() == Qt::Horizontal) ? QRect(o + x - step / 2, 0, w, 1) : QRect(0, o + x - step / 2, 1, w));
+		}
+	}
+	p.end();
+
+}
+
+void ColorSlider::updateValues()
+{
+	if (!m_doc)
+		return;
+
+	disconnectSlots();
+
+	QSignalBlocker blocker(this);
+
+	double v1, v2, v3, v4;
+
+	switch (m_mode)
+	{
+	case Mode::Opacity:
+		setValue(m_opacity);
+		break;
+	case Mode::Shade:
+		setValue(m_shade);
+		break;
+	case Mode::CMYKCyan:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelCMYK);
+		m_color.getCMYK(&v1, &v2, &v3, &v4);
+		setValue(v1 * maximum());
+		break;
+	case Mode::CMYKMagenta:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelCMYK);
+		m_color.getCMYK(&v1, &v2, &v3, &v4);
+		setValue(v2 * maximum());
+		break;
+	case Mode::CMYKYellow:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelCMYK);
+		m_color.getCMYK(&v1, &v2, &v3, &v4);
+		setValue(v3 * maximum());
+		break;
+	case Mode::CMYKKey:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelCMYK);
+		m_color.getCMYK(&v1, &v2, &v3, &v4);
+		setValue(v4 * maximum());
+		break;
+	case Mode::HSVHue:
+		setValue(ScColorEngine::getRGBColor(m_color, m_doc).hueF() * maximum());
+		break;
+	case Mode::HSVSaturation:
+		setValue(ScColorEngine::getRGBColor(m_color, m_doc).saturationF() * maximum());
+		break;
+	case Mode::HSVValue:
+		setValue(ScColorEngine::getRGBColor(m_color, m_doc).valueF() * maximum());
+		break;
+	case Mode::HLCHue:
+	{
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelLab);
+		m_color.getLab(&v1, &v2, &v3);
+		QLineF lin(QPointF(0.0, 0.0), QPointF(v2, v3));
+		setValue(360 - lin.angle());
+		break;
+	}
+	case Mode::HLCChroma:
+	{
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelLab);
+		m_color.getLab(&v1, &v2, &v3);
+		QLineF lin(QPointF(0.0, 0.0), QPointF(v2, v3));
+		setValue(lin.length());
+		break;
+	}
+	case Mode::HLCLightness:
+	case Mode::LabLightness:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelLab);
+		m_color.getLab(&v1, &v2, &v3);
+		setValue(v1);
+		break;
+	case Mode::LabA:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelLab);
+		m_color.getLab(&v1, &v2, &v3);
+		setValue(v2);
+		break;
+	case Mode::LabB:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelLab);
+		m_color.getLab(&v1, &v2, &v3);
+		setValue(v3);
+		break;
+	case Mode::RGBWebRed:
+	case Mode::RGBRed:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelRGB);
+		m_color.getRGB(&v1, &v2, &v3);
+		setValue(v1 * maximum());
+		break;
+	case Mode::RGBWebGreen:
+	case Mode::RGBGreen:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelRGB);
+		m_color.getRGB(&v1, &v2, &v3);
+		setValue(v2 * maximum());
+		break;
+	case Mode::RGBWebBlue:
+	case Mode::RGBBlue:
+		m_color = ScColorEngine::convertToModel(m_color, m_doc, colorModelRGB);
+		m_color.getRGB(&v1, &v2, &v3);
+		setValue(v3 * maximum());
+		break;
+	}
+
+	connectSlots();
+}
+
+/* ********************************************************************************* *
+ *
+ * Events
+ *
+ * ********************************************************************************* */
+
+void ColorSlider::resizeEvent(QResizeEvent *e)
+{
+	QWidget::resizeEvent(e);
+
+	if (e->oldSize() != e->size())
+		renderMap();
+}
+
+void ColorSlider::showEvent(QShowEvent *e)
+{
+	QWidget::showEvent(e);
+	renderMap();
+}
+
+void ColorSlider::mouseMoveEvent(QMouseEvent *e)
+{
+	if (e->buttons() & Qt::LeftButton)
+	{
+		switch (m_mode)
+		{
+		// Override slider movement for custom steps
+		case Mode::RGBWebBlue:
+		case Mode::RGBWebGreen:
+		case Mode::RGBWebRed:
+			setSliderPosition(pixelPosToRangeValue(e->pos()));
+			break;
+		default:
+			QSlider::mouseMoveEvent(e);
+			break;
+		}
+
+		return;
+	}
+
+	QSlider::mouseMoveEvent(e);
+}
+
+int ColorSlider::pixelPosToRangeValue(QPoint pt) const
+{
+	int pos, sliderMin, sliderMax;
+	if (orientation() == Qt::Horizontal)
+	{
+		pos = pt.x();
+		sliderMin = rect().x();
+		sliderMax = rect().right() - RADIUS * 2 + 1;
+	}
+	else
+	{
+		pos = pt.y();
+		sliderMin = rect().y();
+		sliderMax = rect().bottom() - RADIUS * 2 + 1;
+	}
+
+	return roundToStep(QStyle::sliderValueFromPosition(minimum(), maximum(), pos - sliderMin, sliderMax - sliderMin));
+}
+
diff --git a/scribus/ui/widgets/color_slider.h b/scribus/ui/widgets/color_slider.h
new file mode 100644
index 000000000..b497f18bd
--- /dev/null
+++ b/scribus/ui/widgets/color_slider.h
@@ -0,0 +1,115 @@
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+/*
+For general Scribus copyright and licensing information please refer
+to the COPYING file provided with the program.
+*/
+#ifndef COLOR_SLIDER_H
+#define COLOR_SLIDER_H
+
+#include <QPointer>
+#include <QSlider>
+#include "sccolor.h"
+#include "scribusapi.h"
+
+class QPaintEvent;
+class QWidget;
+class ScribusDoc;
+
+class SCRIBUS_API ColorSlider : public QSlider
+{
+    Q_OBJECT
+
+friend class ScribusProxyStyle;
+
+public:
+	enum Mode
+	{
+		RGBRed = 0,
+		RGBGreen,
+		RGBBlue,
+		RGBWebRed,
+		RGBWebGreen,
+		RGBWebBlue,
+		LabLightness,
+		LabA,
+		LabB,
+		CMYKCyan,
+		CMYKMagenta,
+		CMYKYellow,
+		CMYKKey,
+		HLCHue,
+		HLCLightness,
+		HLCChroma,
+		HSVHue,
+		HSVSaturation,
+		HSVValue,
+		Shade,
+		Opacity
+	};
+
+	ColorSlider(QWidget* parent = nullptr);
+	ColorSlider(Mode mode, QWidget* parent = nullptr);
+
+	ScColor color() { return m_color; }
+	double opacity() { return m_opacity; } // 0.0 - 100.0
+	double shade() { return m_shade; } // 0.0 - 100.0
+	Mode mode() { return m_mode; }
+	bool isDynamic() { return m_dynamicColor; }
+
+public slots:
+	void setDoc(ScribusDoc *doc);
+    void setMode(ColorSlider::Mode mode);
+	void setValues(const QString &colorName, double opacity, double shade);
+	void setValues(ScColor color, double opacity, double shade);
+	void setColor(const QString &colorName);
+	void setColor(ScColor color);
+	void setShade(double shade);
+	void setOpacity(double opacity);
+	void setIsDynamic(double dynamic);
+
+private slots:
+	void updateColor();
+
+protected:
+    void resizeEvent(QResizeEvent* e);
+	void showEvent(QShowEvent *e);
+	void mouseMoveEvent(QMouseEvent *e);
+
+private:
+	QPointer<ScribusDoc> m_doc;
+    QGradientStops m_stops;
+    ScColor m_color;
+	double m_opacity {100.0}; // full opacity
+	double m_shade {100.0}; // no shade
+	int m_colorSteps {100};
+	bool m_dynamicColor {true};
+	Mode m_mode {RGBRed};
+    QImage m_background;
+
+	int pixelPosToRangeValue(QPoint pos) const;
+	int roundToStep(int value) const
+	{
+		int mod = maximum() / m_colorSteps;
+		return (value / mod) * mod;
+	}
+
+    void setup();
+    void connectSlots();
+    void disconnectSlots();
+
+    void renderMap();
+    void updateValues();
+
+signals:
+    void colorChanged(ScColor);
+
+};
+
+#endif // COLOR_SLIDER_H
diff --git a/scribus/util_gui.h b/scribus/util_gui.h
index 4ed63b819..9ec661438 100644
--- a/scribus/util_gui.h
+++ b/scribus/util_gui.h
@@ -9,6 +9,8 @@ class VGradient;
 class VColorStop;
 class ScPattern;
 
+const int RADIUS = 6;
+
 void SCRIBUS_API drawCircularHandle(QPainter * painter, QPointF center, qreal width, QBrush background, bool isEnabled = true);
 void SCRIBUS_API drawPointerHandle(QPainter * painter, QPointF pointer, qreal width, QColor background, bool isEnabled = true, bool isEmpty = false);
 void SCRIBUS_API renderCheckerPattern(QPainter *painter, QRectF rect);
colorslider_2025-12-13_01.diff (41,944 bytes)   

cbradney

2025-12-13 22:27

administrator   ~0053372

Updated
colorslider_fixed.diff (20,025 bytes)   
Index: scribus/CMakeLists_Sources.txt
===================================================================
--- scribus/CMakeLists_Sources.txt	(revision 27207)
+++ scribus/CMakeLists_Sources.txt	(working copy)
@@ -567,6 +567,7 @@
 	ui/docks/dock_panelbase.cpp
 	ui/factories/scribusproxystyle.cpp
 	ui/widgets/color_button.cpp
+	ui/widgets/color_slider.cpp
 	ui/widgets/combo_blendmode.cpp
 	ui/widgets/combo_linestyle.cpp
 	ui/widgets/dropdown_button.cpp
Index: scribus/ui/colorpicker/colorpicker_color.cpp
===================================================================
--- scribus/ui/colorpicker/colorpicker_color.cpp	(revision 27207)
+++ scribus/ui/colorpicker/colorpicker_color.cpp	(working copy)
@@ -26,6 +26,9 @@
 
 	languageChange();
 
+	sliderAlpha->setMode(ColorSlider::Opacity);
+	sliderShade->setMode(ColorSlider::Shade);
+
 	connectSlots();
 	connect(sectionSwatches,    &SectionContainer::sizeChanged,				this, &ColorPickerColor::updateSize);
 }
@@ -34,7 +37,9 @@
 {
 	connect(swatches,			&ColorPickerColorSwatches::colorChanged,	this, &ColorPickerColor::updateColorFromSwatches);	
 	connect(numberShade,		&ScrSpinBox::valueChanged,					this, &ColorPickerColor::updateColorShade);
+	connect(sliderShade,		&ColorSlider::valueChanged,					this, &ColorPickerColor::updateColorShade);
 	connect(numberAlpha,		&ScrSpinBox::valueChanged,					this, &ColorPickerColor::updateColorAlpha);
+	connect(sliderAlpha,		&ColorSlider::valueChanged,					this, &ColorPickerColor::updateColorAlpha);
 }
 
 void ColorPickerColor::disconnectSlots()
@@ -41,7 +46,9 @@
 {
 	disconnect(swatches,		&ColorPickerColorSwatches::colorChanged,	this, &ColorPickerColor::updateColorFromSwatches);
 	disconnect(numberShade,		&ScrSpinBox::valueChanged,					this, &ColorPickerColor::updateColorShade);
+	disconnect(sliderShade,		&ColorSlider::valueChanged,					this, &ColorPickerColor::updateColorShade);
 	disconnect(numberAlpha,		&ScrSpinBox::valueChanged,					this, &ColorPickerColor::updateColorAlpha);
+	disconnect(sliderAlpha,		&ColorSlider::valueChanged,					this, &ColorPickerColor::updateColorAlpha);
 }
 
 /* ********************************************************************************* *
@@ -73,8 +80,10 @@
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(false);
 		labelShade->setVisible(false);
+		sliderShade->setVisible(false);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	case Context::Text:
 	case Context::TextBackground:
@@ -82,22 +91,28 @@
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(true);
 		labelShade->setVisible(true);
+		sliderShade->setVisible(true);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	case Context::Fill:
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(true);
 		labelShade->setVisible(true);
+		sliderShade->setVisible(true);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	case Context::Line:
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(true);
 		labelShade->setVisible(true);
+		sliderShade->setVisible(true);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	case Context::FillMask:
 	case Context::LineMask:
@@ -104,8 +119,10 @@
 		sectionSwatches->setVisible(false);
 		numberShade->setVisible(false);
 		labelShade->setVisible(false);
+		sliderShade->setVisible(false);
 		numberAlpha->setVisible(true);
 		labelAlpha->setVisible(true);
+		sliderAlpha->setVisible(true);
 		isMask = true;
 		break;
 	case Context::DropShadow:
@@ -112,8 +129,10 @@
 		sectionSwatches->setVisible(true);
 		numberShade->setVisible(true);
 		labelShade->setVisible(true);
+		sliderShade->setVisible(true);
 		numberAlpha->setVisible(false);
 		labelAlpha->setVisible(false);
+		sliderAlpha->setVisible(false);
 		break;
 	}
 
@@ -183,12 +202,19 @@
 void ColorPickerColor::setDoc(ScribusDoc *doc)
 {
 	m_doc = doc;
+	sliderAlpha->setDoc(m_doc);
+	sliderShade->setDoc(m_doc);
 }
 
 void ColorPickerColor::setColorData(const CPColorData& color)
 {
+	if (!m_doc)
+		return;
+
 	m_color = color;
 
+	bool hasColor = m_color.Name != CommonStrings::tr_NoneColor;
+
 	// update UI + controls
 	QSignalBlocker sigSwatches(swatches);
 	swatches->setCurrentColor(m_color.Name);
@@ -195,12 +221,20 @@
 
 	QSignalBlocker sigShade(numberShade);
 	numberShade->setValue(m_color.Shade);
-	numberShade->setEnabled(m_color.Name != CommonStrings::tr_NoneColor);
+	numberShade->setEnabled(hasColor);
 
 	QSignalBlocker sigAlpha(numberAlpha);
 	numberAlpha->setValue(qRound(100 - (m_color.Opacity * 100)));
-	numberAlpha->setEnabled(m_color.Name != CommonStrings::tr_NoneColor);
+	numberAlpha->setEnabled(hasColor);
 
+	QSignalBlocker sigAlphaSlider(sliderAlpha);
+	sliderAlpha->setEnabled(hasColor);
+	sliderAlpha->setValues(m_color.Name, numberAlpha->value(), numberShade->value());
+
+	QSignalBlocker sigShadeSlider(sliderShade);
+	sliderShade->setEnabled(hasColor);
+	sliderShade->setValues(m_color.Name, numberAlpha->value(), numberShade->value());
+
 }
 
 void ColorPickerColor::languageChange()
@@ -211,14 +245,32 @@
 void ColorPickerColor::updateColorFromSwatches()
 {
 	m_color.Name = swatches->currentColor();
-	numberShade->setEnabled(m_color.Name != CommonStrings::tr_NoneColor);
-	numberAlpha->setEnabled(m_color.Name != CommonStrings::tr_NoneColor);
+	bool hasColor = m_color.Name != CommonStrings::tr_NoneColor;
 
+	numberShade->setEnabled(hasColor);
+	sliderShade->setEnabled(hasColor);
+	sliderShade->setValues(m_color.Name, numberAlpha->value(), numberShade->value());
+	numberAlpha->setEnabled(hasColor);
+	sliderAlpha->setEnabled(hasColor);
+	sliderAlpha->setValues(m_color.Name, numberAlpha->value(), numberShade->value());
+
 	updateColor();
 }
 
 void ColorPickerColor::updateColorShade()
 {
+	QObject* obj = sender();
+	if( obj == sliderShade )
+	{
+		QSignalBlocker sigBlocker(numberShade);
+		numberShade->setValue(sliderShade->value());
+	}
+	else if( obj == numberShade )
+	{
+		QSignalBlocker sigBlocker(sliderShade);
+		sliderShade->setValue(numberShade->value());
+	}
+
 	m_color.Shade = numberShade->value();
 	updateColor();
 }
@@ -225,6 +277,18 @@
 
 void ColorPickerColor::updateColorAlpha()
 {
+	QObject* obj = sender();
+	if( obj == sliderAlpha )
+	{
+		QSignalBlocker sigBlocker(numberAlpha);
+		numberAlpha->setValue(sliderAlpha->value());
+	}
+	else if( obj == numberAlpha )
+	{
+		QSignalBlocker sigBlocker(sliderAlpha);
+		sliderAlpha->setValue(numberAlpha->value());
+	}
+
 	m_color.Opacity = (100 - numberAlpha->value()) / 100.0;
 	updateColor();
 }
Index: scribus/ui/colorpicker/colorpicker_color.ui
===================================================================
--- scribus/ui/colorpicker/colorpicker_color.ui	(revision 27207)
+++ scribus/ui/colorpicker/colorpicker_color.ui	(working copy)
@@ -59,51 +59,58 @@
        </property>
       </widget>
      </item>
-     <item row="1" column="1">
-      <widget class="QLabel" name="labelAlpha">
-       <property name="text">
-        <string>Opacity:</string>
+     <item row="0" column="2">
+      <widget class="ScrSpinBox" name="numberShade">
+       <property name="suffix">
+        <string> %</string>
        </property>
-       <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+       <property name="maximum">
+        <double>100.000000000000000</double>
        </property>
+       <property name="singleStep">
+        <double>10.000000000000000</double>
+       </property>
       </widget>
      </item>
-     <item row="0" column="1">
+     <item row="0" column="0">
       <widget class="QLabel" name="labelShade">
        <property name="text">
         <string>Shade:</string>
        </property>
        <property name="alignment">
-        <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+        <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
        </property>
       </widget>
      </item>
-     <item row="0" column="2">
-      <widget class="ScrSpinBox" name="numberShade">
-       <property name="suffix">
-        <string> %</string>
+     <item row="0" column="1">
+      <widget class="ColorSlider" name="sliderShade">
+       <property name="orientation">
+        <enum>Qt::Orientation::Horizontal</enum>
        </property>
-       <property name="maximum">
-        <double>100.000000000000000</double>
+       <property name="tickPosition">
+        <enum>QSlider::TickPosition::TicksAbove</enum>
        </property>
-       <property name="singleStep">
-        <double>10.000000000000000</double>
+      </widget>
+     </item>
+     <item row="1" column="0">
+      <widget class="QLabel" name="labelAlpha">
+       <property name="text">
+        <string>Opacity:</string>
        </property>
+       <property name="alignment">
+        <set>Qt::AlignmentFlag::AlignRight|Qt::AlignmentFlag::AlignTrailing|Qt::AlignmentFlag::AlignVCenter</set>
+       </property>
       </widget>
      </item>
-     <item row="1" column="0">
-      <spacer name="horizontalSpacer">
+     <item row="1" column="1">
+      <widget class="ColorSlider" name="sliderAlpha">
        <property name="orientation">
-        <enum>Qt::Horizontal</enum>
+        <enum>Qt::Orientation::Horizontal</enum>
        </property>
-       <property name="sizeHint" stdset="0">
-        <size>
-         <width>0</width>
-         <height>0</height>
-        </size>
+       <property name="tickPosition">
+        <enum>QSlider::TickPosition::TicksAbove</enum>
        </property>
-      </spacer>
+      </widget>
      </item>
     </layout>
    </item>
@@ -136,6 +143,11 @@
    <header>ui/colorpicker/colorpicker_color_swatches.h</header>
    <container>1</container>
   </customwidget>
+  <customwidget>
+   <class>ColorSlider</class>
+   <extends>QSlider</extends>
+   <header>ui/widgets/color_slider.h</header>
+  </customwidget>
  </customwidgets>
  <tabstops>
   <tabstop>numberShade</tabstop>
Index: scribus/ui/dasheditor.cpp
===================================================================
--- scribus/ui/dasheditor.cpp	(revision 27207)
+++ scribus/ui/dasheditor.cpp	(working copy)
@@ -55,7 +55,6 @@
  *
  * ********************************************************************************* */
 
-const int RADIUS = 6;
 const double HAIRLINE = 0.001;
 
 DashPreview::DashPreview(QWidget *pa) : QFrame(pa)
Index: scribus/ui/factories/scribusproxystyle.cpp
===================================================================
--- scribus/ui/factories/scribusproxystyle.cpp	(revision 27207)
+++ scribus/ui/factories/scribusproxystyle.cpp	(working copy)
@@ -18,6 +18,8 @@
 
 #include "prefsmanager.h"
 #include "scribus.h"
+#include "ui/widgets/color_slider.h"
+#include "util_gui.h"
 
 // https://doc.qt.io/qt-6/qstyle.html#standardPalette
 QPalette createLightPalette()
@@ -64,6 +66,167 @@
 	return static_cast<ScribusProxyStyle*>(qApp->style());
 }
 
+QRect ScribusProxyStyle::subControlRect(ComplexControl cc, const QStyleOptionComplex *option, SubControl sc, const QWidget *widget) const
+{
+	QRect rect = QProxyStyle::subControlRect(cc, option, sc, widget);
+
+	switch (cc)
+	{
+	case QStyle::ComplexControl::CC_Slider:
+	{
+		const QStyleOptionSlider *soSlider = qstyleoption_cast<const QStyleOptionSlider *>(option);
+
+		if (soSlider && widget->inherits("ColorSlider") )
+		{
+			QRect ret;
+			bool horizontal = soSlider->orientation == Qt::Horizontal;
+			int thickness = RADIUS * 2;
+			int center = (horizontal) ? (soSlider->rect.height() - thickness) / 2 : (soSlider->rect.width() - thickness) / 2;
+			int sliderOffset = (soSlider->tickPosition != QSlider::NoTicks) ? thickness : center;
+
+			switch (sc)
+			{
+			case SC_SliderHandle:
+			{
+				int len = thickness + 2; //proxy()->pixelMetric(PM_SliderLength, styleOption, widget);
+				int sliderPos = sliderPositionFromValue(soSlider->minimum, soSlider->maximum, soSlider->sliderPosition, (horizontal ? soSlider->rect.width() : soSlider->rect.height()) - len, soSlider->upsideDown);
+
+				if (horizontal)
+					ret.setRect(soSlider->rect.x() + sliderPos, soSlider->rect.y() + sliderOffset, len, thickness);
+				else
+					ret.setRect(soSlider->rect.x() + 1, soSlider->rect.y() + sliderPos, thickness, len);
+
+				break;
+			}
+			case SC_SliderGroove:
+				if (horizontal)
+					ret.setRect(soSlider->rect.x(), soSlider->rect.y() + sliderOffset + 1, soSlider->rect.width(), thickness - 3);
+				else
+					ret.setRect(soSlider->rect.x() + 2, soSlider->rect.y(), thickness - 3, soSlider->rect.height());
+
+				break;
+			case SC_SliderTickmarks:
+			{
+				int tickOffset = (soSlider->tickPosition != QSlider::NoTicks) ? thickness : 0;
+
+				if (soSlider->tickPosition != QSlider::NoTicks)
+				{
+					if (horizontal)
+						ret.setRect(soSlider->rect.x() + 1, soSlider->rect.y(), soSlider->rect.width() - 3, thickness);
+					else
+						ret.setRect(soSlider->rect.x() + tickOffset, soSlider->rect.y() + 1, thickness, soSlider->rect.height() - 3);
+				}
+				break;
+			}
+			default:
+				break;
+			}
+
+			return visualRect(soSlider->direction, soSlider->rect, ret);
+		}
+	}
+	break;
+	default:
+		break;
+	}
+
+	return rect;
+}
+
+void ScribusProxyStyle::drawComplexControl(ComplexControl cc, const QStyleOptionComplex *opt, QPainter *painter, const QWidget *widget) const
+{
+	switch (cc)
+	{
+	case QStyle::ComplexControl::CC_Slider:
+	{
+		const ColorSlider *wdg = qobject_cast<const ColorSlider *>(widget);
+		const QStyleOptionSlider *soSlider = qstyleoption_cast<const QStyleOptionSlider *>(opt);
+
+		if (soSlider && wdg)
+		{
+			painter->save();
+			painter->setRenderHint(QPainter::Antialiasing, true);
+
+			QRect groove = subControlRect(CC_Slider, soSlider, SC_SliderGroove, widget);
+			QRect handle = subControlRect(CC_Slider, soSlider, SC_SliderHandle, widget);
+			QRect tickmarks = subControlRect(CC_Slider, soSlider, SC_SliderTickmarks, widget);
+
+			// Draw Slider
+			if ((soSlider->subControls & SC_SliderGroove) && groove.isValid())
+			{
+				double radius = (soSlider->orientation == Qt::Horizontal) ? groove.height() / 2 + 0.5 : groove.width() / 2 + 0.5;
+
+				QPainterPath border;
+				border.addRoundedRect(groove.toRectF().adjusted(0.5, 0.5, -0.5, -0.5), radius, radius);
+
+				// Draw Slider Gradients
+				painter->setClipPath(border);
+				if (wdg->backgroundImage().isNull() || !widget->isEnabled())
+					painter->fillRect(groove, widget->palette().color(QPalette::Window));
+				else
+				{
+					renderCheckerPattern(painter, groove);
+					painter->drawImage(groove, wdg->backgroundImage());
+				}
+				painter->setClipping(false);
+
+				// Draw Slider Border
+				painter->setPen(widget->palette().color(QPalette::Mid));
+				painter->setBrush(Qt::NoBrush);
+				painter->drawPath(border);
+				painter->setPen(Qt::NoPen);
+			}
+
+			// Draw Handle
+			if ((soSlider->subControls & SC_SliderHandle) && handle.isValid())
+				drawCircularHandle(painter, handle.center() + QPointF(0.5, 0.5), RADIUS * 2, Qt::NoBrush, widget->isEnabled());
+
+			// Draw Tickmarks
+			if ((soSlider->subControls & SC_SliderTickmarks) && tickmarks.isValid() && soSlider->tickInterval > 0)
+			{
+				QFont font = widget->font();
+				font.setPixelSize(8);
+				painter->setFont(font);
+
+				if (soSlider->orientation == Qt::Horizontal)
+				{
+					double ratio = double(tickmarks.width() - (2 * RADIUS)) / double(soSlider->tickInterval);
+
+					for (int x = 0; x <= soSlider->tickInterval; x++)
+					{
+						QRect tick(double(x) * ratio + RADIUS + 1, tickmarks.bottom() - 3, 1, 2);
+						QString label = QString::number((x * soSlider->maximum / soSlider->tickInterval) - abs(soSlider->minimum));
+						int offset = QFontMetrics(painter->font()).size(Qt::TextSingleLine, label).width() / 2;
+
+						painter->setPen(widget->palette().color(QPalette::WindowText));
+						painter->drawText(qBound(0, tick.left() - offset, tickmarks.width()), tickmarks.bottom() - 4, label);
+						painter->fillRect(tick, widget->palette().color(QPalette::WindowText));
+					}
+				}
+				else
+				{
+					double ratio = double(tickmarks.height() - (2 * RADIUS)) / double(soSlider->tickInterval);
+
+					for (int y = 0; y <= soSlider->tickInterval; y++)
+					{
+						QRect tick(tickmarks.left() + 2, double(y) * ratio + RADIUS + 1, 2, 1);
+						painter->fillRect(tick, widget->palette().color(QPalette::WindowText));
+					}
+				}
+			}
+
+			painter->restore();
+			return;
+		}
+	}
+		break;
+	default:
+		break;
+	}
+
+	QProxyStyle::drawComplexControl(cc, opt, painter, widget);
+}
+
 bool ScribusProxyStyle::eventFilter(QObject *object, QEvent *event)
 {
 	if (object == qApp && event->type() == QEvent::ThemeChange)
Index: scribus/ui/factories/scribusproxystyle.h
===================================================================
--- scribus/ui/factories/scribusproxystyle.h	(revision 27207)
+++ scribus/ui/factories/scribusproxystyle.h	(working copy)
@@ -20,6 +20,9 @@
 
 	static ScribusProxyStyle* instance();
 
+	QRect subControlRect(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, QStyle::SubControl sc, const QWidget *widget = nullptr) const override;
+	void drawComplexControl(QStyle::ComplexControl cc, const QStyleOptionComplex *opt, QPainter *painter, const QWidget *widget) const override;
+
 protected:
 	bool eventFilter(QObject *object, QEvent *event) override;
 
Index: scribus/ui/gradientpreview.cpp
===================================================================
--- scribus/ui/gradientpreview.cpp	(revision 27207)
+++ scribus/ui/gradientpreview.cpp	(working copy)
@@ -29,7 +29,6 @@
 #include "gradientpreview.h"
 #include "util_gui.h"
 
-const int RADIUS = 6;
 const int GRADIENT_HEIGHT = 20;
 
 /* ********************************************************************************* *
Index: scribus/ui/widgets/color_slider.cpp
===================================================================
--- scribus/ui/widgets/color_slider.cpp	(revision 27207)
+++ scribus/ui/widgets/color_slider.cpp	(working copy)
@@ -18,8 +18,6 @@
  *
  * ********************************************************************************* */
 
-const int RADIUS = 6;
-
 ColorSlider::ColorSlider(QWidget *parent): ColorSlider(Mode::RGBRed, parent) {}
 
 ColorSlider::ColorSlider(Mode mode, QWidget *parent): QSlider(parent)
@@ -553,7 +551,7 @@
 	QSignalBlocker blocker(this);
 
 	double v1, v2, v3, v4;
-	int multplier = 1;
+	// int multplier = 1;
 
 	switch (m_mode)
 	{
Index: scribus/ui/widgets/color_slider.h
===================================================================
--- scribus/ui/widgets/color_slider.h	(revision 27207)
+++ scribus/ui/widgets/color_slider.h	(working copy)
@@ -55,11 +55,12 @@
 	ColorSlider(QWidget* parent = nullptr);
 	ColorSlider(Mode mode, QWidget* parent = nullptr);
 
-	const ScColor& color() const{ return m_color; }
+	const ScColor& color() const { return m_color; }
 	double opacity() const { return m_opacity; } // 0.0 - 100.0
 	double shade() const { return m_shade; } // 0.0 - 100.0
 	Mode mode() const { return m_mode; }
 	bool isDynamic() const { return m_dynamicColor; }
+	const QImage& backgroundImage() const { return m_background; }
 
 public slots:
 	void setDoc(ScribusDoc *doc);
Index: scribus/util_gui.h
===================================================================
--- scribus/util_gui.h	(revision 27207)
+++ scribus/util_gui.h	(working copy)
@@ -9,6 +9,8 @@
 class VColorStop;
 class ScPattern;
 
+const int RADIUS = 6;
+
 void SCRIBUS_API drawCircularHandle(QPainter * painter, QPointF center, qreal width, QBrush background, bool isEnabled = true);
 void SCRIBUS_API drawPointerHandle(QPainter * painter, QPointF pointer, qreal width, QColor background, bool isEnabled = true, bool isEmpty = false);
 void SCRIBUS_API renderCheckerPattern(QPainter *painter, QRectF rect);
colorslider_fixed.diff (20,025 bytes)   

Issue History

Date Modified Username Field Change
2025-05-13 20:33 nitramr New Issue
2025-05-13 20:33 nitramr Status new => assigned
2025-05-13 20:33 nitramr Assigned To => nitramr
2025-05-13 20:33 nitramr File Added: color_slider.zip
2025-05-13 20:33 nitramr File Added: colorslider_2025-05-13_01.patch
2025-05-13 20:33 nitramr File Added: preview.png
2025-05-13 21:06 cbradney Note Added: 0052524
2025-05-13 21:06 cbradney File Added: image.png
2025-05-30 05:47 ale Relationship added child of 0017540
2025-10-25 14:16 nitramr Note Added: 0053120
2025-10-25 14:16 nitramr File Added: colorslider_2025-10-25_01.patch
2025-10-25 14:16 nitramr File Added: scribus.zip
2025-10-25 14:16 nitramr File Added: colorslider_mac.png
2025-10-25 19:36 cbradney Note Added: 0053124
2025-10-27 18:58 nitramr Relationship added duplicate of 0006434
2025-12-13 19:04 nitramr Note Added: 0053370
2025-12-13 19:04 nitramr File Added: colorslider_2025-12-13_01.diff
2025-12-13 22:27 cbradney Note Added: 0053372
2025-12-13 22:27 cbradney File Added: colorslider_fixed.diff