From e6215c7233c0fb437a81b51c8a8a30bb53eef65f Mon Sep 17 00:00:00 2001 From: Tomaž Vajngerl Date: Thu, 25 May 2023 00:53:22 +0900 Subject: tdf#153361 improve theme color generation in color picker MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 Reviewed-by: Tomaž Vajngerl --- 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(-) 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 maData; +}; + class SVXCORE_DLLPUBLIC PaletteManager { const sal_uInt16 mnMaxRecentColors; @@ -49,6 +69,7 @@ class SVXCORE_DLLPUBLIC PaletteManager ColorSelectFunction maColorSelectFunction; std::unique_ptr m_pColorDlg; + std::optional 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 #include +#include #include #include #include @@ -33,8 +33,6 @@ #include #include #include -#include -#include #include #include #include @@ -48,15 +46,32 @@ #include +#include +#include +#include +#include + namespace { -// Luminance modulation for the 6 effect presets. -// 10000 is the default. -constexpr const std::array g_aLumMods = { 10000, 2000, 4000, 6000, 7500, 5000 }; +constexpr const std::array g_aPercentBlack = { 0, 50, 35, 25, 15, 5 }; +constexpr const std::array g_aLumModsBlack = { 10'000, 5'000, 6'500, 7'500, 8'500, 9'500 }; +constexpr const std::array g_aLumOffsBlack = { 0, 5'000, 3'500, 2'500, 1'500, 0'500 }; + +constexpr const std::array g_aPercentLow = { 0, 90, 75, 50, 25, 10 }; +constexpr const std::array g_aLumModsLow = { 10'000, 1'000, 2'500, 5'000, 7'500, 9'000 }; +constexpr const std::array g_aLumOffsLow = { 0, 9'000, 7'500, 5'000, 2'500, 1'000 }; + +constexpr const std::array g_aPercent = { 0, 80, 60, 40, -25, -50 }; +constexpr const std::array g_aLumMods = { 10'000, 2'000, 4'000, 6'000, 7'500, 5'000 }; +constexpr const std::array 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 g_aLumOffs = { 0, 8000, 6000, 4000, 0, 0 }; +constexpr const std::array g_aPercentHigh = { 0, -10, -25, -50, -75, -90 }; +constexpr const std::array g_aLumModsHigh = { 10'000, 9'000, 7'500, 5'000, 2'500, 1'000 }; +constexpr const std::array g_aLumOffsHigh = { 0, 0, 0, 0, 0, 0 }; + +constexpr const std::array g_aPercentWhite = { 0, -5, -15, -25, -35, -50 }; +constexpr const std::array g_aLumModsWhite = { 10'000, 9'500, 8'500, 7'500, 6'500, 5'000 }; +constexpr const std::array 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 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 aColorNames = { + const std::array 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); } -- cgit