cui/qa/uitest/dialogs/chardlg.py | 4 cui/source/tabpages/tpcolor.cxx | 8 + include/svx/PaletteManager.hxx | 25 ++++- include/svx/strings.hrc | 15 +-- svx/source/tbxctrls/PaletteManager.cxx | 155 +++++++++++++++++++++++++++------ svx/source/tbxctrls/tbcontrl.cxx | 14 +- 6 files changed, 174 insertions(+), 47 deletions(-)
New commits: commit e6215c7233c0fb437a81b51c8a8a30bb53eef65f Author: Tomaž Vajngerl <tomaz.vajng...@collabora.co.uk> AuthorDate: Thu May 25 00:53:22 2023 +0900 Commit: Tomaž Vajngerl <qui...@gmail.com> CommitDate: Thu Jun 1 18:25:58 2023 +0200 tdf#153361 improve theme color generation in color picker The theme color generator needs to take color luminocity into account, so that the very dark or very light colors are properly shaded. Otherwise a too dark color will generate a too dark (almost black) color variant, or a too light a too light (almost white) color variant. However those colors aren't useful. Change-Id: Id803a8f6f1a79cbc822ed2d7faca9bec228c0188 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/152237 Tested-by: Tomaž Vajngerl <qui...@gmail.com> Reviewed-by: Tomaž Vajngerl <qui...@gmail.com> diff --git a/cui/qa/uitest/dialogs/chardlg.py b/cui/qa/uitest/dialogs/chardlg.py index 4a756cf6c979..85e60f766238 100644 --- a/cui/qa/uitest/dialogs/chardlg.py +++ b/cui/qa/uitest/dialogs/chardlg.py @@ -115,8 +115,8 @@ class Test(UITestCase): # AssertionError: 10000 != 2000 # i.e. the effects where not applied, luminance modulation was the default instead of a # custom value. - self.assertEqual(portion.CharColorLumMod, 2000) - self.assertEqual(portion.CharColorLumOff, 8000) + self.assertEqual(portion.CharColorLumMod, 5000) + self.assertEqual(portion.CharColorLumOff, 5000) def testSvxCharEffectsPageWriter(self): # Start Writer. diff --git a/cui/source/tabpages/tpcolor.cxx b/cui/source/tabpages/tpcolor.cxx index 470b1c13a866..bdefcf10114d 100644 --- a/cui/source/tabpages/tpcolor.cxx +++ b/cui/source/tabpages/tpcolor.cxx @@ -522,7 +522,13 @@ IMPL_LINK(SvxColorTabPage, SelectValSetHdl_Impl, ValueSet*, pValSet, void) aNamedColor.m_aColor = aColor; if (bThemePaletteSelected) { - PaletteManager::GetThemeIndexLumModOff(nPos, aNamedColor.m_nThemeIndex, aNamedColor.m_nLumMod, aNamedColor.m_nLumOff); + sal_uInt16 nThemeIndex; + sal_uInt16 nEffectIndex; + if (PaletteManager::GetThemeAndEffectIndex(nPos, nThemeIndex, nEffectIndex)) + { + aNamedColor.m_nThemeIndex = nThemeIndex; + maPaletteManager.GetLumModOff(nThemeIndex, nEffectIndex, aNamedColor.m_nLumMod, aNamedColor.m_nLumOff); + } } ChangeColor(aNamedColor, false); diff --git a/include/svx/PaletteManager.hxx b/include/svx/PaletteManager.hxx index c6d1712a12f4..37d22b92ec64 100644 --- a/include/svx/PaletteManager.hxx +++ b/include/svx/PaletteManager.hxx @@ -32,6 +32,26 @@ namespace com::sun::star::uno { class XComponentContext; } namespace svx { class ToolboxButtonColorUpdaterBase; } namespace weld { class Window; } +enum class ThemePaletteColorType +{ + Black, + White, + Low, + High, + Normal +}; + +struct ThemePaletteData +{ + ThemePaletteColorType meType = ThemePaletteColorType::Normal; + Color maColor; +}; + +struct ThemePaletteCollection +{ + std::array<ThemePaletteData, 12> maData; +}; + class SVXCORE_DLLPUBLIC PaletteManager { const sal_uInt16 mnMaxRecentColors; @@ -49,6 +69,7 @@ class SVXCORE_DLLPUBLIC PaletteManager ColorSelectFunction maColorSelectFunction; std::unique_ptr<SvColorDialog> m_pColorDlg; + std::optional<ThemePaletteCollection> moThemePaletteCollection; PaletteManager(const PaletteManager* pClone); public: @@ -79,8 +100,8 @@ public: PaletteManager* Clone() const; - static void GetThemeIndexLumModOff(sal_uInt16 nItemId, sal_Int16& rThemeIndex, - sal_Int16& rLumMod, sal_Int16& rLumOff); + static bool GetThemeAndEffectIndex(sal_uInt16 nItemId, sal_uInt16& rThemeIndex, sal_uInt16& rEffectIndex); + bool GetLumModOff(sal_uInt16 nThemeIndex, sal_uInt16 nEffect, sal_Int16& rLumMod, sal_Int16& rLumOff); static void DispatchColorCommand(const OUString& aCommand, const NamedColor& rColor); }; diff --git a/include/svx/strings.hrc b/include/svx/strings.hrc index e0573c485d99..249d01060a39 100644 --- a/include/svx/strings.hrc +++ b/include/svx/strings.hrc @@ -1120,10 +1120,10 @@ #define RID_SVXSTR_DOC_COLORS NC_("RID_SVXSTR_DOC_COLORS", "Document colors") #define RID_SVXSTR_THEME_COLORS NC_("RID_SVXSTR_THEME_COLORS", "Theme colors") #define RID_SVXSTR_DOC_COLOR_PREFIX NC_("RID_SVXSTR_DOC_COLOR_PREFIX", "Document Color") -#define RID_SVXSTR_THEME_COLOR1 NC_("RID_SVXSTR_THEME_COLOR1", "Background - Dark 1") -#define RID_SVXSTR_THEME_COLOR2 NC_("RID_SVXSTR_THEME_COLOR2", "Text - Light 1") -#define RID_SVXSTR_THEME_COLOR3 NC_("RID_SVXSTR_THEME_COLOR3", "Background - Dark 2") -#define RID_SVXSTR_THEME_COLOR4 NC_("RID_SVXSTR_THEME_COLOR4", "Text - Light 2") +#define RID_SVXSTR_THEME_COLOR1 NC_("RID_SVXSTR_THEME_COLOR1", "Dark 1") +#define RID_SVXSTR_THEME_COLOR2 NC_("RID_SVXSTR_THEME_COLOR2", "Light 1") +#define RID_SVXSTR_THEME_COLOR3 NC_("RID_SVXSTR_THEME_COLOR3", "Dark 2") +#define RID_SVXSTR_THEME_COLOR4 NC_("RID_SVXSTR_THEME_COLOR4", "Light 2") #define RID_SVXSTR_THEME_COLOR5 NC_("RID_SVXSTR_THEME_COLOR5", "Accent 1") #define RID_SVXSTR_THEME_COLOR6 NC_("RID_SVXSTR_THEME_COLOR6", "Accent 2") #define RID_SVXSTR_THEME_COLOR7 NC_("RID_SVXSTR_THEME_COLOR7", "Accent 3") @@ -1132,11 +1132,8 @@ #define RID_SVXSTR_THEME_COLOR10 NC_("RID_SVXSTR_THEME_COLOR10", "Accent 6") #define RID_SVXSTR_THEME_COLOR11 NC_("RID_SVXSTR_THEME_COLOR11", "Hyperlink") #define RID_SVXSTR_THEME_COLOR12 NC_("RID_SVXSTR_THEME_COLOR12", "Followed Hyperlink") -#define RID_SVXSTR_THEME_EFFECT1 NC_("RID_SVXSTR_THEME_EFFECT1", "%1, 80% Lighter") -#define RID_SVXSTR_THEME_EFFECT2 NC_("RID_SVXSTR_THEME_EFFECT2", "%1, 60% Lighter") -#define RID_SVXSTR_THEME_EFFECT3 NC_("RID_SVXSTR_THEME_EFFECT3", "%1, 40% Lighter") -#define RID_SVXSTR_THEME_EFFECT4 NC_("RID_SVXSTR_THEME_EFFECT4", "%1, 25% Darker") -#define RID_SVXSTR_THEME_EFFECT5 NC_("RID_SVXSTR_THEME_EFFECT5", "%1, 50% Darker") +#define RID_SVXSTR_THEME_EFFECT_LIGHTER NC_("RID_SVXSTR_THEME_EFFECT_LIGHTER", "$THEME_NAME, $PERCENTAGE% Lighter") +#define RID_SVXSTR_THEME_EFFECT_DARKER NC_("RID_SVXSTR_THEME_EFFECT_DARKER", "$THEME_NAME, $PERCENTAGE% Darker") #define RID_SVX_EXTRUSION_BAR NC_("RID_SVX_EXTRUSION_BAR", "Extrusion") #define RID_SVXSTR_UNDO_APPLY_EXTRUSION_ON_OFF NC_("RID_SVXSTR_UNDO_APPLY_EXTRUSION_ON_OFF", "Apply Extrusion On/Off") diff --git a/svx/source/tbxctrls/PaletteManager.cxx b/svx/source/tbxctrls/PaletteManager.cxx index 26df330b9501..1f3ab3345359 100644 --- a/svx/source/tbxctrls/PaletteManager.cxx +++ b/svx/source/tbxctrls/PaletteManager.cxx @@ -17,9 +17,9 @@ * the License at http://www.apache.org/licenses/LICENSE-2.0 . */ -#include <memory> #include <svx/PaletteManager.hxx> +#include <basegfx/color/bcolortools.hxx> #include <comphelper/propertyvalue.hxx> #include <tools/urlobj.hxx> #include <osl/file.hxx> @@ -33,8 +33,6 @@ #include <vcl/svapp.hxx> #include <vcl/settings.hxx> #include <comphelper/sequence.hxx> -#include <stack> -#include <set> #include <officecfg/Office/Common.hxx> #include <com/sun/star/frame/XDispatchProvider.hpp> #include <com/sun/star/frame/XDispatch.hpp> @@ -48,15 +46,32 @@ #include <palettes.hxx> +#include <memory> +#include <array> +#include <stack> +#include <set> + namespace { -// Luminance modulation for the 6 effect presets. -// 10000 is the default. -constexpr const std::array<sal_Int16, 6> g_aLumMods = { 10000, 2000, 4000, 6000, 7500, 5000 }; +constexpr const std::array<sal_Int16, 6> g_aPercentBlack = { 0, 50, 35, 25, 15, 5 }; +constexpr const std::array<sal_Int16, 6> g_aLumModsBlack = { 10'000, 5'000, 6'500, 7'500, 8'500, 9'500 }; +constexpr const std::array<sal_Int16, 6> g_aLumOffsBlack = { 0, 5'000, 3'500, 2'500, 1'500, 0'500 }; + +constexpr const std::array<sal_Int16, 6> g_aPercentLow = { 0, 90, 75, 50, 25, 10 }; +constexpr const std::array<sal_Int16, 6> g_aLumModsLow = { 10'000, 1'000, 2'500, 5'000, 7'500, 9'000 }; +constexpr const std::array<sal_Int16, 6> g_aLumOffsLow = { 0, 9'000, 7'500, 5'000, 2'500, 1'000 }; + +constexpr const std::array<sal_Int16, 6> g_aPercent = { 0, 80, 60, 40, -25, -50 }; +constexpr const std::array<sal_Int16, 6> g_aLumMods = { 10'000, 2'000, 4'000, 6'000, 7'500, 5'000 }; +constexpr const std::array<sal_Int16, 6> g_aLumOffs = { 0, 8'000, 6'000, 4'000, 0, 0 }; -// Luminance offset for the 6 effect presets. -// 0 is the default. -constexpr const std::array<sal_Int16, 6> g_aLumOffs = { 0, 8000, 6000, 4000, 0, 0 }; +constexpr const std::array<sal_Int16, 6> g_aPercentHigh = { 0, -10, -25, -50, -75, -90 }; +constexpr const std::array<sal_Int16, 6> g_aLumModsHigh = { 10'000, 9'000, 7'500, 5'000, 2'500, 1'000 }; +constexpr const std::array<sal_Int16, 6> g_aLumOffsHigh = { 0, 0, 0, 0, 0, 0 }; + +constexpr const std::array<sal_Int16, 6> g_aPercentWhite = { 0, -5, -15, -25, -35, -50 }; +constexpr const std::array<sal_Int16, 6> g_aLumModsWhite = { 10'000, 9'500, 8'500, 7'500, 6'500, 5'000 }; +constexpr const std::array<sal_Int16, 6> g_aLumOffsWhite = { 0, 0, 0, 0, 0, 0 }; } PaletteManager::PaletteManager() : @@ -66,6 +81,7 @@ PaletteManager::PaletteManager() : mnColorCount(0), mpBtnUpdater(nullptr), maColorSelectFunction(PaletteManager::DispatchColorCommand) + { SfxObjectShell* pDocSh = SfxObjectShell::Current(); if(pDocSh) @@ -165,19 +181,53 @@ bool PaletteManager::IsThemePaletteSelected() const return mnCurrentPalette == mnNumOfPalettes - 2; } -void PaletteManager::GetThemeIndexLumModOff(sal_uInt16 nItemId, sal_Int16& rThemeIndex, - sal_Int16& rLumMod, sal_Int16& rLumOff) +bool PaletteManager::GetThemeAndEffectIndex(sal_uInt16 nItemId, sal_uInt16& rThemeIndex, sal_uInt16& rEffectIndex) { - // Each column is the same color with different effects. + // Each column is the same color with different effects. rThemeIndex = nItemId % 12; - // Each row is the same effect with different colors. - rLumMod = g_aLumMods[nItemId / 12]; - rLumOff = g_aLumOffs[nItemId / 12]; + rEffectIndex = nItemId / 12; + if (rEffectIndex > 5) + return false; + return true; +} + +bool PaletteManager::GetLumModOff(sal_uInt16 nThemeIndex, sal_uInt16 nEffect, sal_Int16& rLumMod, sal_Int16& rLumOff) +{ + if (!moThemePaletteCollection) + return false; + + auto const& aThemeColorData = moThemePaletteCollection->maData[nThemeIndex]; + + switch (aThemeColorData.meType) + { + case ThemePaletteColorType::Black: + rLumMod = g_aLumModsBlack[nEffect]; + rLumOff = g_aLumOffsBlack[nEffect]; + break; + case ThemePaletteColorType::White: + rLumMod = g_aLumModsWhite[nEffect]; + rLumOff = g_aLumOffsWhite[nEffect]; + break; + case ThemePaletteColorType::Low: + rLumMod = g_aLumModsLow[nEffect]; + rLumOff = g_aLumOffsLow[nEffect]; + break; + case ThemePaletteColorType::High: + rLumMod = g_aLumModsHigh[nEffect]; + rLumOff = g_aLumOffsHigh[nEffect]; + break; + case ThemePaletteColorType::Normal: + rLumMod = g_aLumMods[nEffect]; + rLumOff = g_aLumOffs[nEffect]; + break; + } + return true; } void PaletteManager::ReloadColorSet(SvxColorValueSet &rColorSet) { + moThemePaletteCollection.reset(); if( mnCurrentPalette == 0) { rColorSet.Clear(); @@ -201,13 +251,7 @@ void PaletteManager::ReloadColorSet(SvxColorValueSet &rColorSet) rColorSet.Clear(); if (aColors.size() >= 12) { - std::vector<OUString> aEffectNames = { - SvxResId(RID_SVXSTR_THEME_EFFECT1), SvxResId(RID_SVXSTR_THEME_EFFECT2), - SvxResId(RID_SVXSTR_THEME_EFFECT3), SvxResId(RID_SVXSTR_THEME_EFFECT4), - SvxResId(RID_SVXSTR_THEME_EFFECT5), - }; - - std::vector<OUString> aColorNames = { + const std::array<OUString, 12> aColorNames = { SvxResId(RID_SVXSTR_THEME_COLOR1), SvxResId(RID_SVXSTR_THEME_COLOR2), SvxResId(RID_SVXSTR_THEME_COLOR3), SvxResId(RID_SVXSTR_THEME_COLOR4), SvxResId(RID_SVXSTR_THEME_COLOR5), SvxResId(RID_SVXSTR_THEME_COLOR6), @@ -217,22 +261,77 @@ void PaletteManager::ReloadColorSet(SvxColorValueSet &rColorSet) }; sal_uInt16 nItemId = 0; + + moThemePaletteCollection = ThemePaletteCollection(); + for (size_t nColor = 0; nColor < aColorNames.size(); ++nColor) + { + Color aColor = aColors[nColor]; + basegfx::BColor aBColor = basegfx::utils::rgb2hsl(aColor.getBColor()); + double aLuminanceValue = aBColor.getBlue() * 255.0; + moThemePaletteCollection->maData[nColor].maColor = aColor; + + if (aLuminanceValue < 0.5) + moThemePaletteCollection->maData[nColor].meType = ThemePaletteColorType::Black; + else if (aLuminanceValue > 254.5) + moThemePaletteCollection->maData[nColor].meType = ThemePaletteColorType::White; + else if (aLuminanceValue < 50.5) + moThemePaletteCollection->maData[nColor].meType = ThemePaletteColorType::Low; + else if (aLuminanceValue > 203.5) + moThemePaletteCollection->maData[nColor].meType = ThemePaletteColorType::High; + else + moThemePaletteCollection->maData[nColor].meType = ThemePaletteColorType::Normal; + } + // Each row is one effect type (no effect + each type). - for (size_t nEffect = 0; nEffect < aEffectNames.size() + 1; ++nEffect) + for (size_t nEffect : {0, 1, 2, 3, 4, 5}) { // Each column is one color type. for (size_t nColor = 0; nColor < aColorNames.size(); ++nColor) { - Color aColor = aColors[nColor]; - aColor.ApplyLumModOff(g_aLumMods[nEffect], g_aLumOffs[nEffect]); + auto const& aThemeColorData = moThemePaletteCollection->maData[nColor]; + Color aColor = aThemeColorData.maColor; + sal_Int16 nColorTemplateValue = 0; + switch (aThemeColorData.meType) + { + case ThemePaletteColorType::Black: + nColorTemplateValue = g_aPercentBlack[nEffect]; + break; + case ThemePaletteColorType::White: + nColorTemplateValue = g_aPercentWhite[nEffect]; + break; + case ThemePaletteColorType::Low: + nColorTemplateValue = g_aPercentLow[nEffect]; + break; + case ThemePaletteColorType::High: + nColorTemplateValue = g_aPercentHigh[nEffect]; + break; + case ThemePaletteColorType::Normal: + nColorTemplateValue = g_aPercent[nEffect]; + break; + } + + sal_Int16 nLumMod = 10'000; + sal_Int16 nLumOff = 0; + GetLumModOff(nColor, nEffect, nLumMod, nLumOff); + aColor.ApplyLumModOff(nLumMod, nLumOff); + OUString aColorName; - if (nEffect == 0) + if (nColorTemplateValue > 0) { - aColorName = aColorNames[nColor]; + OUString aTemplate = SvxResId(RID_SVXSTR_THEME_EFFECT_LIGHTER); + aColorName = aTemplate.replaceAll("$THEME_NAME", aColorNames[nColor]); + aColorName = aColorName.replaceAll("$PERCENTAGE", OUString::number(std::abs(nColorTemplateValue))); + + } + else if (nColorTemplateValue < 0) + { + OUString aTemplate = SvxResId(RID_SVXSTR_THEME_EFFECT_DARKER); + aColorName = aTemplate.replaceAll("$THEME_NAME", aColorNames[nColor]); + aColorName = aColorName.replaceAll("$PERCENTAGE", OUString::number(std::abs(nColorTemplateValue))); } else { - aColorName = aEffectNames[nEffect - 1].replaceAll("%1", aColorNames[nColor]); + aColorName = aColorNames[nColor]; } rColorSet.InsertItem(nItemId++, aColor, aColorName); } diff --git a/svx/source/tbxctrls/tbcontrl.cxx b/svx/source/tbxctrls/tbcontrl.cxx index c27216cdedd5..eb7ab2e61410 100644 --- a/svx/source/tbxctrls/tbcontrl.cxx +++ b/svx/source/tbxctrls/tbcontrl.cxx @@ -2236,14 +2236,18 @@ IMPL_LINK(ColorWindow, SelectHdl, ValueSet*, pColorSet, void) bool bThemePaletteSelected = mxPaletteManager->IsThemePaletteSelected(); sal_uInt16 nSelectedItemId = pColorSet->GetSelectedItemId(); - maMenuButton.set_inactive(); - if (bThemePaletteSelected) { - PaletteManager::GetThemeIndexLumModOff(nSelectedItemId, aNamedColor.m_nThemeIndex, - aNamedColor.m_nLumMod, - aNamedColor.m_nLumOff); + sal_uInt16 nThemeIndex; + sal_uInt16 nEffectIndex; + if (PaletteManager::GetThemeAndEffectIndex(nSelectedItemId, nThemeIndex, nEffectIndex)) + { + aNamedColor.m_nThemeIndex = nThemeIndex; + mxPaletteManager->GetLumModOff(nThemeIndex, nEffectIndex, aNamedColor.m_nLumMod, aNamedColor.m_nLumOff); + } } + + maMenuButton.set_inactive(); aColorSelectFunction(sCommand, aNamedColor); }