Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • videolan/vlc
  • chouquette/vlc
  • bakiewicz.marek122/vlc
  • devnexen/vlc
  • rohanrajpal/vlc
  • blurrrb/vlc
  • gsoc/gsoc2019/darkapex/vlc
  • b1ue/vlc
  • fkuehne/vlc
  • magsoft/vlc
  • chub/vlc
  • cramiro9/vlc
  • robUx4/vlc
  • rom1v/vlc
  • akshayaky/vlc
  • tmk907/vlc
  • akymaster/vlc
  • govind.sharma/vlc
  • psilokos/vlc
  • xjbeta/vlc
  • jahan/vlc
  • 1480c1/vlc
  • amanchande/vlc
  • aaqib/vlc
  • rist/vlc
  • apol/vlc
  • mindfreeze/vlc
  • alexandre-janniaux/vlc
  • sandsmark/vlc
  • jagannatharjun/vlc
  • gsoc/gsoc2020/matiaslgonzalez/vlc
  • gsoc/gsoc2020/jagannatharjun/vlc
  • mstorsjo/vlc
  • gsoc/gsoc2020/vedenta/vlc
  • gsoc/gsoc2020/arnav-ishaan/vlc
  • gsoc/gsoc2020/andreduong/vlc
  • fuzun/vlc
  • gsoc/gsoc2020/vatsin/vlc
  • gsoc/gsoc2020/sagid/vlc
  • yaron/vlc
  • Phoenix/vlc
  • Garf/vlc
  • ePiratWorkarounds/vlc
  • tguillem/vlc
  • jnqnfe/vlc
  • mdc/vlc
  • Vedaa/vlc
  • rasa/vlc
  • quink/vlc
  • yealo/vlc
  • aleksey_ak/vlc
  • ePirat/vlc
  • ilya.yanok/vlc
  • asenat/vlc
  • m/vlc
  • bunjee/vlc
  • BLumia/vlc
  • sagudev/vlc
  • hamedmonji30/vlc
  • nullgemm/vlc
  • DivyamAhuja/vlc
  • thesamesam/vlc
  • dag7/vlc
  • snehil101/vlc
  • haasn/vlc
  • jbk/vlc
  • ValZapod/vlc
  • mfkl/vlc
  • WangChuan/vlc
  • core1024/vlc
  • GhostVaibhav/vlc
  • dfuhrmann/vlc
  • davide.prade/vlc
  • tmatth/vlc
  • Courmisch/vlc
  • zouya/vlc
  • hpi/vlc
  • EwoutH/vlc
  • aleung27/vlc
  • hengwu0/vlc
  • saladin/vlc
  • ashuio/vlc
  • richselwood/vlc
  • verma16Ayush/vlc
  • chemicalflash/vlc
  • PoignardAzur/vlc
  • huangjieNT/vlc
  • Blake-Haydon/vlc
  • AnuthaDev/vlc
  • gsoc/gsoc2021/mpd/vlc
  • nicolas_lequec/vlc
  • sambassaly/vlc
  • thresh/vlc
  • bonniegong/vlc
  • myaashish/vlc
  • stavros.vagionitis/vlc
  • ileoo/vlc
  • louis-santucci/vlc
  • cchristiansen/vlc
  • sabyasachi07/vlc
  • AbduAmeen/vlc
  • ashishb0410/vlc
  • urbanhusky/vlc
  • davidepietrasanta/vlc
  • riksleutelstad/vlc
  • jeremyVignelles/vlc
  • komh/vlc
  • iamjithinjohn/vlc
  • JohannesKauffmann/vlc2
  • kunglao/vlc
  • natzberg/vlc
  • jill/vlc
  • cwendling/vlc
  • adufou/vlc
  • ErwanAirone/vlc
  • HasinduDilshan10/vlc
  • vagrantc/vlc
  • rafiv/macos-bigsur-icon
  • Aymeriic/vlc
  • saranshg20/vlc
  • metzlove24/vlc
  • linkfanel/vlc
  • Ds886/vlc
  • metehan-arslan/vlc
  • Skantes/vlc
  • kgsandundananjaya96/vlc
  • mitchcapper/vlc
  • advaitgupta/vlc
  • StefanBruens/vlc
  • ratajs/vlc
  • T.M.F.B.3761/vlc
  • m222059/vlc
  • casemerrick/vlc
  • joshuaword2alt/vlc
  • sjwaddy/vlc
  • dima/vlc
  • Ybalrid/vlc
  • umxprime/vlc
  • eschmidt/vlc
  • vannieuwenhuysenmichelle/vlc
  • badcf00d/vlc
  • wesinator/vlc
  • louis/vlc
  • xqq/vlc
  • EmperorYP7/vlc
  • NicoLiam/vlc
  • loveleen/vlc
  • rofferom/vlc
  • rbultje/vlc
  • TheUnamed/vlc
  • pratiksharma341/vlc
  • Saurab17/vlc
  • purist.coder/vlc
  • Shuicheng/vlc
  • mdrrubel292/vlc
  • silverbleu00/vlc
  • metif12/vlc
  • asher-m/vlc
  • jeffk/vlc
  • Brandonbr1/vlc
  • beautyyuyanli/vlc
  • rego21/vlc
  • muyangren907/vlc
  • collectionbylawrencejason/vlc
  • evelez/vlc
  • GSMgeeth/vlc
  • Oneric/vlc
  • TJ5/vlc
  • XuanTung95/vlc
  • darrenjenny21/vlc
  • Trenly/vlc
  • RockyTDR/vlc
  • mjakubowski/vlc
  • caprica/vlc
  • ForteFrankie/vlc
  • seannamiller19/vlc
  • junlon2006/vlc
  • kiwiren6666/vlc
  • iuseiphonexs/vlc
  • fenngtun/vlc
  • Rajdutt999/vlc
  • typx/vlc
  • leon.vitanos/vlc
  • robertogarci0938/vlc
  • gsoc/gsoc2022/luc65r/vlc-mpd
  • skeller/vlc
  • MCJack123/vlc
  • luc65r/vlc-mpd
  • popov895/vlc
  • claucambra/vlc
  • brad/vlc
  • matthewmurua88/vlc
  • Tomas8874/vlc
  • philenotfound/vlc
  • makita-do3/vlc
  • LZXCorp/vlc
  • mar0x/vlc
  • senojetkennedy0102/vlc
  • shaneb243/vlc
  • ahmadbader/vlc
  • rajduttcse26/vlc-audio-filters
  • Juniorzito8415/vlc
  • achernyakov/vlc
  • lucasjetgroup/vlc
  • pupdoggy666/vlc
  • gmde9363/vlc
  • alexnwayne/vlc
  • bahareebrahimi781/vlc
  • hamad633666/vlc
  • umghof3112/vlc
  • joe0199771874/vlc
  • Octocats66666666/vlc
  • jjm_223/vlc
  • btech10110.19/vlc
  • sunnykfc028/vlc-audio-filters
  • loic/vlc
  • nguyenminhducmx1/vlc
  • JanekKrueger/vlc
  • bstubbington2/vlc
  • rcombs/vlc
  • Ordissimo/vlc
  • king7532/vlc
  • noobsauce101/vlc
  • schong0525/vlc
  • myQwil/vlc
  • apisbg91/vlc
  • geeboy0101017/vlc
  • kim.faughey/vlc
  • nurupo/vlc
  • yyusea/vlc
  • 0711235879.khco/vlc
  • ialo/vlc
  • iloveyeye2/vlc
  • gdtdftdqtd/vlc
  • leandroconsiglio/vlc
  • AndyHTML2012/vlc
  • ncz/vlc
  • lucenticus/vlc
  • knr1931/vlc
  • kjoonlee/vlc
  • chandrakant100/vlc-qt
  • johge42/vlc
  • polter/vlc
  • hexchain/vlc
  • Tushwrld/vlc
  • mztea928/vlc
  • jbelloncastro/vlc
  • alvinhochun/vlc
  • ghostpiratecrow/vlc
  • ujjwaltwitx/vlc
  • alexsonarin06/vlc
  • adrianbon76/vlc
  • altsod/vlc
  • damien.lucas44/vlc
  • dmytrivtaisa/vlc
  • utk202/vlc
  • aaxhrj/vlc
  • thomas.hermes/vlc
  • structurenewworldorder/vlc
  • slomo/vlc
  • wantlamy/vlc
  • musc.o3cminc/vlc
  • thebarshablog/vlc
  • kerrick/vlc
  • kratos142518/vlc
  • leogps/vlc
  • vacantron/vlc
  • luna_koly/vlc
  • Ratio2/vlc
  • anuoshemohammad/vlc
  • apsun/vlc
  • aaa1115910/vlc
  • alimotmoyo/vlc
  • Ambossmann/vlc
  • Sam-LearnsToCode/vlc
  • Chilledheart/vlc
  • Labnann/vlc
  • ktcoooot1/vlc
  • mohit-marathe/vlc
  • johnddx/vlc
  • manstabuk/vlc
  • Omar-ahmed314/vlc
  • vineethkm/vlc
  • 9Enemi86/vlc
  • radoslav.m.panteleev/vlc
  • ashishami2002/vlc
  • Corbax/vlc
  • firnasahmed/vlc
  • pelayarmalam4/vlc
  • c0ff330k/vlc
  • shikhindahikar/vlc
  • l342723951/vlc
  • christianschwandner/vlc
  • douniwan5788/vlc
  • 7damian7/vlc
  • ferdnyc/vlc
  • f.ales1/vlc
  • pandagby/vlc
  • BaaBaa/vlc
  • jewe37/vlc
  • w00drow/vlc
  • russelltg/vlc
  • ironicallygod/vlc
  • soumyaDghosh/vlc
  • linzihao1999/vlc
  • deyayush6/vlc
  • mibi88/vlc
  • newabdallah10/vlc
  • jhorbincolombia/vlc
  • rimvihaqueshupto/vlc
  • andrewkhon98/vlc
  • fab78/vlc
  • lapaz17/vlc
  • amanna13/vlc
  • mdakram28/vlc
  • 07jw1980/vlc
  • sohamgupta/vlc
  • Eson-Jia1/vlc
  • Sumou/vlc
  • vikram-kangotra/vlc
  • chalice191/vlc
  • olivercalder/vlc
  • aaasg4001/vlc
  • zipdox/vlc
  • kwizart/vlc
  • Dragon-S/vlc
  • jdemeule/vlc
  • gabriel_lt/vlc
  • locutusofborg/vlc
  • sammirata/vlc-librist
  • another/vlc
  • Benjamin_Loison/vlc
  • ahmedmoselhi/vlc
  • petergaal/vlc
  • huynhsontung/vlc
  • dariusmihut/vlc
  • tvermaashutosh/vlc
  • buti/vlc
  • Niram7777/vlc
  • rohan-here/vlc
  • balaji-sivasakthi/vlc
  • rlindner81/vlc
  • Kakadus/vlc
  • djain/vlc
  • ABBurmeister/vlc
  • craighuggins/vlc
  • orbea/vlc
  • maxos/vlc
  • aakarshmj/vlc
  • kblaschke/vlc
  • ankitm/vlc
  • advait-0/vlc
  • mohak2003/vlc
  • yselkowitz/vlc
  • AZM999/vlc-azm
  • andrey.turkin/vlc
  • Disha-Baghel/vlc
  • nowrep/vlc
  • Apeng/vlc
  • Choucroute_melba/vlc
  • autra/vlc
  • eclipseo/vlc
  • fhuber/vlc
  • olafhering/vlc
  • sdasda7777/vlc
  • 1div0/vlc
  • skosnits/vlc-extended-playlist-support
  • dnicolson/vlc
  • Timshel/vlc
  • octopols/vlc
  • MangalK/vlc
  • nima64/vlc
  • misawai/vlc
  • Alexander-Wilms/vlc
  • Maxime2/vlc-fork-for-visualizer
  • ww/vlc
  • jeske/vlc
  • sgross-emlix/vlc
  • morenonatural/vlc
  • freakingLovesVLC/vlc
  • borisgolovnev/vlc
  • mpromonet/vlc
  • diogo.simao-marques/vlc
  • masstock/vlc
  • pratikpatel8982/vlc
  • hugok79/vlc
  • longervision/vlc
  • abhiudaysurya/vlc
  • rishabhgarg/vlc
  • tumic/vlc
  • cart/vlc
  • shubham442/vlc
  • Aditya692005/vlc
  • sammirata/vlc4
  • syrykh/vlc
  • Vvorcun/macos-new-icon
  • AyaanshC/vlc
  • nasso/vlc
  • Quark/vlc
  • sebastinas/vlc
  • rhstone/vlc
  • talregev/vlc
  • Managor/vlc
403 results
Show changes
Commits on Source (17)
Showing
with 2935 additions and 232 deletions
......@@ -3924,6 +3924,7 @@ AC_ARG_ENABLE([qt],
])
])
have_qt5_x11="no"
have_qt5_gtk="no"
AS_IF([test "${enable_qt}" != "no"], [
PKG_CHECK_MODULES([QT], [Qt5Core >= 5.11.0 Qt5Widgets Qt5Gui Qt5Quick Qt5QuickWidgets Qt5QuickControls2 Qt5Svg], [
PKG_CHECK_MODULES([QT5_X11], [Qt5X11Extras], [
......@@ -3981,6 +3982,14 @@ AS_IF([test "${enable_qt}" != "no"], [
AS_IF([test "${SYS}" = "darwin" -a "${HAVE_OSX}" = 1], [
PKG_CHECK_MODULES([QT5_PLATFORM_PLUGINS], [qcocoa])
])
])
AS_IF([test "${SYS}" != "mingw32" -a "${SYS}" != "darwin"], [
dnl gtk theme provider
PKG_CHECK_MODULES([GTK3], [gtk+-3.0 >= 3.20], [
have_qt5_gtk=yes
], [:])
])
], [
AS_IF([test -n "${enable_qt}"],[
......@@ -4008,6 +4017,7 @@ AC_SUBST([QT_VERSION])
AM_CONDITIONAL([ENABLE_QT], [test "$enable_qt" != "no"])
AM_CONDITIONAL([HAVE_QT5_X11], [test "${have_qt5_x11}" = "yes"])
AM_CONDITIONAL([HAVE_QT5_WAYLAND], [test "${have_qt5_wayland}" = "yes"])
AM_CONDITIONAL([HAVE_QT5_GTK], [test "${have_qt5_gtk}" = "yes"])
dnl
dnl detect kde4-config patch (used for kde solids).
......
......@@ -78,6 +78,9 @@ endif
if QT_QML_DEBUG
libqt_plugin_la_CPPFLAGS += -DQT_QML_DEBUG
endif
if HAVE_QT5_GTK
libqt_plugin_la_CPPFLAGS += -DQT5_HAS_GTK
endif
libqt_plugin_la_SOURCES = \
gui/qt/qt.cpp gui/qt/qt.hpp gui/qt/plugins.hpp \
......@@ -240,6 +243,10 @@ libqt_plugin_la_SOURCES = \
gui/qt/network/networkmediamodel.hpp \
gui/qt/network/servicesdiscoverymodel.cpp \
gui/qt/network/servicesdiscoverymodel.hpp \
gui/qt/style/systempalette.cpp \
gui/qt/style/systempalette.hpp \
gui/qt/style/defaultthemeproviders.hpp \
gui/qt/style/systempalettethemeprovider.cpp \
gui/qt/player/input_models.cpp gui/qt/player/input_models.hpp \
gui/qt/player/player_controller.cpp gui/qt/player/player_controller.hpp gui/qt/player/player_controller_p.hpp \
gui/qt/player/player_controlbar_model.cpp gui/qt/player/player_controlbar_model.hpp \
......@@ -285,7 +292,6 @@ libqt_plugin_la_SOURCES = \
gui/qt/util/sortfilterproxymodel.cpp \
gui/qt/util/sortfilterproxymodel.hpp \
gui/qt/util/soutchain.cpp gui/qt/util/soutchain.hpp \
gui/qt/util/systempalette.cpp gui/qt/util/systempalette.hpp \
gui/qt/util/validators.cpp gui/qt/util/validators.hpp \
gui/qt/util/varcommon_p.hpp \
gui/qt/util/varchoicemodel.cpp gui/qt/util/varchoicemodel.hpp \
......@@ -302,6 +308,8 @@ libqt_plugin_la_SOURCES = \
gui/qt/widgets/native/animators.cpp \
gui/qt/widgets/native/animators.hpp \
gui/qt/widgets/native/customwidgets.cpp gui/qt/widgets/native/customwidgets.hpp \
gui/qt/widgets/native/csdthemeimage.cpp \
gui/qt/widgets/native/csdthemeimage.hpp \
gui/qt/widgets/native/interface_widgets.cpp \
gui/qt/widgets/native/interface_widgets.hpp \
gui/qt/widgets/native/navigation_attached.cpp \
......@@ -318,7 +326,8 @@ libqt_plugin_la_SOURCES += \
gui/qt/maininterface/mainctx_win32.cpp \
gui/qt/maininterface/mainctx_win32.hpp \
gui/qt/maininterface/compositor_win7.cpp \
gui/qt/maininterface/compositor_win7.hpp
gui/qt/maininterface/compositor_win7.hpp \
gui/qt/style/windowsthemeprovider.cpp
if HAVE_DCOMP
libqt_plugin_la_SOURCES += \
......@@ -446,6 +455,7 @@ nodist_libqt_plugin_la_SOURCES = \
gui/qt/network/networksourcesmodel.moc.cpp \
gui/qt/network/networkmediamodel.moc.cpp \
gui/qt/network/servicesdiscoverymodel.moc.cpp \
gui/qt/style/systempalette.moc.cpp \
gui/qt/player/input_models.moc.cpp \
gui/qt/player/player_controller.moc.cpp \
gui/qt/player/player_controlbar_model.moc.cpp \
......@@ -471,7 +481,6 @@ nodist_libqt_plugin_la_SOURCES = \
gui/qt/util/renderer_manager.moc.cpp \
gui/qt/util/selectable_list_model.moc.cpp \
gui/qt/util/sortfilterproxymodel.moc.cpp \
gui/qt/util/systempalette.moc.cpp \
gui/qt/util/validators.moc.cpp \
gui/qt/util/varchoicemodel.moc.cpp \
gui/qt/util/variables.moc.cpp \
......@@ -479,6 +488,7 @@ nodist_libqt_plugin_la_SOURCES = \
gui/qt/util/qmlinputitem.moc.cpp \
gui/qt/util/qsgroundedrectangularimagenode.moc.cpp \
gui/qt/widgets/native/animators.moc.cpp \
gui/qt/widgets/native/csdthemeimage.moc.cpp \
gui/qt/widgets/native/customwidgets.moc.cpp \
gui/qt/widgets/native/interface_widgets.moc.cpp \
gui/qt/widgets/native/navigation_attached.moc.cpp \
......@@ -964,6 +974,8 @@ libqt_plugin_la_QML = \
gui/qt/widgets/qml/AcrylicBackground.qml \
gui/qt/widgets/qml/AnimatedBackground.qml \
gui/qt/widgets/qml/CoverShadow.qml \
gui/qt/widgets/qml/CSDThemeButton.qml \
gui/qt/widgets/qml/CSDThemeButtonSet.qml \
gui/qt/widgets/qml/CSDWindowButton.qml \
gui/qt/widgets/qml/CSDWindowButtonSet.qml \
gui/qt/widgets/qml/CSDTitlebarTapNDrapHandler.qml \
......@@ -1073,6 +1085,25 @@ gui/qt/resources.cpp: $(lib_qt_plugin_la_QRC) $(libqt_plugin_la_RES) $(libqt_plu
endif
if HAVE_QT5_GTK
libqt_gtktheme_plugin_la_CXXFLAGS = $(AM_CXXFLAGS) $(GTK3_CFLAGS)
libqt_gtktheme_CPPFLAGS = $(AM_CPPFLAGS)
libqt_gtktheme_plugin_la_LDFLAGS = $(AM_LDFLAGS)
libqt_gtktheme_plugin_la_LIBADD = $(GTK3_LIBS)
libqt_gtktheme_plugin_la_SOURCES = \
gui/qt/style/gtkthemeprovider/gtk_compat.cpp \
gui/qt/style/gtkthemeprovider/gtk_compat.h \
gui/qt/style/gtkthemeprovider/gtkthemeprovider.cpp \
gui/qt/style/gtkthemeprovider/gtk_util.cpp \
gui/qt/style/gtkthemeprovider/gtk_util.h \
gui/qt/style/gtkthemeprovider/nav_button_provider_gtk.cpp \
gui/qt/style/gtkthemeprovider/nav_button_provider_gtk.h \
gui/qt/style/gtkthemeprovider/scoped_gobject.h
if ENABLE_QT
gui_LTLIBRARIES += libqt_gtktheme_plugin.la
endif
endif
if ENABLE_QT
gui_LTLIBRARIES += libqt_plugin.la
BUILT_SOURCES += $(nodist_libqt_plugin_la_SOURCES)
......
......@@ -192,9 +192,6 @@ void FirstRunWizard::updateColorLabel( QAbstractButton* btn )
case ColorSchemeModel::Night:
ui.explainerLabel->setText( qtr( "<i>VLC will automatically use dark mode</i>" ) );
break;
case ColorSchemeModel::Auto:
ui.explainerLabel->setText( qtr( "<i>VLC will automatically switch to dark mode accordingly with system settings</i>") );
break;
}
}
......
......@@ -36,7 +36,7 @@
#include "util/imageluminanceextractor.hpp"
#include "util/i18n.hpp"
#include "util/keyhelper.hpp"
#include "util/systempalette.hpp"
#include "style/systempalette.hpp"
#include "util/sortfilterproxymodel.hpp"
#include "util/navigation_history.hpp"
#include "util/qmlinputitem.hpp"
......@@ -56,6 +56,7 @@
#include "menus/qml_menu_wrapper.hpp"
#include "widgets/native/csdthemeimage.hpp"
#include "widgets/native/roundimage.hpp"
#include "widgets/native/navigation_attached.hpp"
#include "widgets/native/viewblockingrectangle.hpp"
......@@ -227,7 +228,6 @@ void MainUI::registerQMLTypes()
qmlRegisterSingletonType<PlayerController>(uri, versionMajor, versionMinor, "Player", SingletonRegisterHelper<PlayerController>::callback);
qmlRegisterSingletonType<I18n>(uri, versionMajor, versionMinor, "I18n", SingletonRegisterHelper<I18n>::callback);
qmlRegisterSingletonType<DialogsProvider>(uri, versionMajor, versionMinor, "DialogsProvider", SingletonRegisterHelper<DialogsProvider>::callback);
qmlRegisterSingletonType<SystemPalette>(uri, versionMajor, versionMinor, "SystemPalette", SingletonRegisterHelper<SystemPalette>::callback);
qmlRegisterSingletonType<DialogErrorModel>(uri, versionMajor, versionMinor, "DialogErrorModel", SingletonRegisterHelper<DialogErrorModel>::callback);
qmlRegisterSingletonType<QmlKeyHelper>(uri, versionMajor, versionMinor, "KeyHelper", SingletonRegisterHelper<QmlKeyHelper>::callback);
qmlRegisterSingletonType<EffectsImageProvider>(uri, versionMajor, versionMinor, "Effects", SingletonRegisterHelper<EffectsImageProvider>::callback);
......@@ -242,6 +242,8 @@ void MainUI::registerQMLTypes()
qRegisterMetaType<VLCTick>();
qmlRegisterUncreatableType<VLCTick>(uri, versionMajor, versionMinor, "VLCTick", "");
qmlRegisterUncreatableType<ColorSchemeModel>(uri, versionMajor, versionMinor, "ColorSchemeModel", "");
qmlRegisterType<SystemPalette>(uri, versionMajor, versionMinor, "SystemPalette");
qmlRegisterType<CSDThemeImage>(uri, versionMajor, versionMinor, "CSDThemeImage");
qRegisterMetaType<QmlInputItem>();
......
......@@ -212,7 +212,9 @@ FocusScope {
}
height: VLCStyle.globalToolbar_height
active: root._showCSD
source: "qrc:///widgets/CSDWindowButtonSet.qml"
source: VLCStyle.theme.hasCSDImage
? "qrc:///widgets/CSDThemeButtonSet.qml"
: "qrc:///widgets/CSDWindowButtonSet.qml"
}
}
......
......@@ -339,10 +339,19 @@ FocusScope{
active: root.showCSD
enabled: root.showCSD
visible: root.showCSD
source: "qrc:///widgets/CSDWindowButtonSet.qml"
source: VLCStyle.theme.hasCSDImage
? "qrc:///widgets/CSDThemeButtonSet.qml"
: "qrc:///widgets/CSDWindowButtonSet.qml"
onLoaded: {
item.color = Qt.binding(function() { return root.colors.playerFg })
item.hoverColor = Qt.binding(function() { return root.colors.windowCSDButtonDarkBg })
if (VLCStyle.theme.hasCSDImage) {
if (item.hasOwnProperty("color"))
item.color = Qt.binding(function() { return VLCStyle.colors.topBanner })
} else {
if (item.hasOwnProperty("color"))
item.color = Qt.binding(function() { return root.colors.playerFg })
if (item.hasOwnProperty("hoverColor"))
item.hoverColor = Qt.binding(function() { return root.colors.windowCSDButtonDarkBg })
}
}
Connections {
......
......@@ -63,6 +63,7 @@ extern "C" char **environ;
#else
# include "maininterface/mainctx.hpp" /* MainCtx creation */
#endif
#include "style/defaultthemeproviders.hpp"
#include "dialogs/extensions/extensions_manager.hpp" /* Extensions manager */
#include "dialogs/plugins/addons_manager.hpp" /* Addons manager */
#include "dialogs/help/help.hpp" /* Launch Update */
......@@ -404,6 +405,18 @@ vlc_module_begin ()
set_capability( "vout window", 0 )
set_callback( WindowOpen )
#ifdef _WIN32
add_submodule ()
set_capability( "qt theme provider", 10 )
set_callback( WindowsThemeProviderOpen )
set_description( "Qt Windows theme" )
add_shortcut("qt-themeprovider-windows")
#endif
add_submodule()
set_capability("qt theme provider", 1)
set_description( "Qt basic system theme" )
set_callback( SystemPaletteThemeProviderOpen )
add_shortcut("qt-themeprovider-systempalette")
vlc_module_end ()
/*****************************************/
......
......@@ -17,22 +17,16 @@
*****************************************************************************/
import QtQuick 2.11
import org.videolan.vlc 0.1 as VLC
import org.videolan.vlc 0.1
Item {
id: colors_id
function blendColors( a, b, blend ) {
return Qt.rgba( a.r * blend + b.r * (1. - blend),
a.g * blend + b.g * (1. - blend),
a.b * blend + b.b * (1. - blend),
a.a * blend + b.a * (1. - blend))
}
/* required*/ property var palette
function setColorAlpha( c, alpha )
{
return Qt.rgba(c.r, c.g, c.b, alpha)
}
//"alias" ColorHelper functions
property var blendColors: palette.blendColors
property var setColorAlpha: palette.setColorAlpha
function getBgColor(selected, hovered, focus)
{
......@@ -46,276 +40,135 @@ Item {
return "transparent"
}
property bool isThemeDark: false
readonly property bool isThemeDark: palette.isDark
property color text: VLC.SystemPalette.text;
property color textInactive: VLC.SystemPalette.textInactive;
property color textDisabled: VLC.SystemPalette.textDisabled;
readonly property color text: palette.text;
readonly property color textInactive: palette.textInactive;
readonly property color textDisabled: palette.textDisabled;
property color caption: setColorAlpha(text, .4)
property color menuCaption: setColorAlpha(text, .6)
readonly property color caption: setColorAlpha(text, .4)
readonly property color menuCaption: setColorAlpha(text, .6)
property color bg: VLC.SystemPalette.base;
property color bgInactive: VLC.SystemPalette.baseInactive;
readonly property color bg: palette.bg;
readonly property color bgInactive: palette.bgInactive;
//for alternate rows
property color bgAlt: VLC.SystemPalette.alternateBase;
property color bgAltInactive: VLC.SystemPalette.alternateBaseInactive;
readonly property color bgAlt: palette.bgAlt;
readonly property color bgAltInactive: palette.bgAltInactive;
property color bgHover: VLC.SystemPalette.highlight;
property color bgHoverText: VLC.SystemPalette.highlightText;
property color bgHoverInactive: VLC.SystemPalette.highlightInactive;
property color bgHoverTextInactive: VLC.SystemPalette.highlightTextInactive;
readonly property color bgHover: palette.bgHover;
readonly property color bgHoverText: palette.bgHoverText;
readonly property color bgHoverInactive: palette.bgHoverInactive;
readonly property color bgHoverTextInactive: palette.bgHoverTextInactive;
property color bgFocus: (isThemeDark) ? white : black
readonly property color bgFocus: palette.bgFocus
// Banner
property color border: (isThemeDark) ? "#303030" : "#e0e0e0"
readonly property color border: palette.border
// Button
property color button: VLC.SystemPalette.button
readonly property color button: palette.button
property color buttonHover: (isThemeDark) ? "#303030" : "#f2f2f2"
readonly property color buttonHover: palette.buttonHover
property color buttonText: VLC.SystemPalette.buttonText
property color buttonTextHover: bgFocus
readonly property color buttonText: palette.buttonText
readonly property color buttonTextHover: bgFocus
property color buttonBorder: blendColors(VLC.SystemPalette.button, VLC.SystemPalette.buttonText, 0.8)
readonly property color buttonBorder: blendColors(palette.button, palette.buttonText, 0.8)
// ButtonBanner (BannerTabButton)
property color buttonBannerDark: "#a6a6a6"
readonly property color buttonBannerDark: "#a6a6a6"
property color buttonBanner: (isThemeDark) ? buttonBannerDark : "#666666"
readonly property color buttonBanner: palette.buttonBanner
// ButtonPrimary (ActionButtonPrimary)
property color buttonPrimaryHover: (isThemeDark) ? "#e67A00" : "#e65609"
readonly property color buttonPrimaryHover: palette.buttonPrimaryHover
// ButtonPlayer (IconControlButton)
property color buttonPlayer: (isThemeDark) ? "#e5e5e5" : "#484848"
readonly property color buttonPlayer: palette.buttonPlayer
// ButtonPlay (ControlButtons)
property color buttonPlayA: "#f89a06"
property color buttonPlayB: "#e25b01"
readonly property color buttonPlayA: "#f89a06"
readonly property color buttonPlayB: "#e25b01"
property color buttonPlayIcon: "#333333"
readonly property color buttonPlayIcon: "#333333"
// GridItem
// NOTE: This needs to contrast with the background because we have no border.
property color grid: (isThemeDark) ? "#272727" : "#ededed"
readonly property color grid: palette.grid
property color gridSelect: (isThemeDark) ? "#303030" : "#e5e5e5"
readonly property color gridSelect: palette.gridSelect
// ListItem
property color listHover: (isThemeDark) ? "#272727" : "#e9e9e9"
readonly property color listHover: palette.listHover
// TrackItem (CheckedDelegate)
property color trackItem: "#303030"
property color trackItemHover: "#2a2a2a"
readonly property color trackItem: palette.darkGrey800
readonly property color trackItemHover: palette.darkGrey600
// TextField
property color textField: (isThemeDark) ? "#6f6f6f" : "#999999"
property color textFieldHover: (isThemeDark) ? "#b7b7b7" : "#4c4c4c"
readonly property color textField: palette.textField
readonly property color textFieldHover: palette.textFieldHover
property color icon: isThemeDark ? white : "#616161"
readonly property color icon: palette.icon
property color textActiveSource: "red";
readonly property color textActiveSource: "red";
property color topBanner: VLC.SystemPalette.window
property color topBannerHover: VLC.SystemPalette.highlight
readonly property color topBanner: palette.topBanner
property color lowerBanner: bg
readonly property color lowerBanner: palette.lowerBanner
property color volsliderbg: "#bdbebf"
property color volbelowmid: "#99d299"
property color volabovemid: "#14d214"
property color volhigh: "#ffc70f"
property color volmax: "#f5271d"
readonly property color volsliderbg: "#bdbebf"
readonly property color volbelowmid: "#99d299"
readonly property color volabovemid: "#14d214"
readonly property color volhigh: "#ffc70f"
readonly property color volmax: "#f5271d"
property color playerFg: text
property color playerFgInactive: textInactive
property color playerControlBarFg: playerFg
property color playerBg: bg
property color playerSeekBar: Qt.lighter(playerBg, 1.6180)
property color playerBorder: buttonText
readonly property color playerFg: text
readonly property color playerFgInactive: textInactive
readonly property color playerControlBarFg: playerFg
readonly property color playerBg: bg
readonly property color playerSeekBar: Qt.lighter(playerBg, 1.6180)
readonly property color playerBorder: buttonText
property color separator: blendColors(bg, text, .95)
property color roundPlayCoverBorder: "#979797"
readonly property color separator: blendColors(bg, text, .95)
// playlist
property color plItemHovered: topBannerHover
property color plItemSelected: blendColors(plItemHovered, plItemFocused, 0.5)
property color plItemFocused: isThemeDark ? "#1E1E1E" : "#EDEDED"
readonly property color roundPlayCoverBorder: "#979797"
// basic color definitions for color blending:
property color black: "black"
property color white: "white"
readonly property color black: "black"
readonly property color white: "white"
// glow colors:
property color glowColor: setColorAlpha(blendColors(bg, black, 0.8), 0.35)
property color glowColorBanner: setColorAlpha(blendColors(topBanner, black, isThemeDark ? 0.25 : 0.35), 0.25)
readonly property color glowColor: setColorAlpha(blendColors(bg, black, 0.8), 0.35)
readonly property color glowColorBanner: setColorAlpha(blendColors(topBanner, black, isThemeDark ? 0.25 : 0.35), 0.25)
property color sliderBarMiniplayerBgColor: isThemeDark ? "#FF929292" : "#FFEEEEEE"
readonly property color sliderBarMiniplayerBgColor: palette.sliderBarMiniplayerBgColor
property color tooltipTextColor: isThemeDark ? white : black
property color tooltipColor: isThemeDark ? black : white
readonly property color tooltipTextColor: palette.tooltipTextColor
readonly property color tooltipColor: palette.tooltipColor
//vlc orange
property color accent: (isThemeDark) ? "#ff8800" : "#ff610a"
property color accentText: "#ffffff";
readonly property color accent: palette.accent
property color alert: "#d70022";
readonly property color alert: palette.alert;
property color buffer: "#696969";
readonly property color buffer: "#696969";
property color seekpoint: "red";
property color record: "red";
readonly property color seekpoint: "red";
readonly property color record: "red";
property color windowCSDButtonDarkBg: "#80484848"
property color windowCSDButtonLightBg: "#80DADADA"
property color windowCSDButtonBg: isThemeDark ? windowCSDButtonDarkBg : windowCSDButtonLightBg
property color expandDelegate
state: {
switch (VLC.MainCtx.colorScheme.scheme) {
case VLC.ColorSchemeModel.System:
return "system"
case VLC.ColorSchemeModel.Day:
return "day"
case VLC.ColorSchemeModel.Night:
return "night"
default:
console.assert(false, "Unknown color scheme")
}
}
readonly property color windowCSDButtonBg: palette.windowCSDButtonBg
states: [
//other styles are provided for testing purpose
State {
name: "day"
PropertyChanges {
target: colors_id
text: "#232627"
textInactive: "#7f8c8d"
bg: "#fafafa"
bgInactive: "#fcfdfc"
bgAlt: "#ededed"
bgAltInactive: "#ededed"
bgHover: "#ededed"
bgHoverText: text
bgHoverInactive: "#3daee9"
bgHoverTextInactive: text
bgFocus: "black"
button: "#eff0f1";
buttonText: "#232627";
buttonBorder: blendColors(button, buttonText, 0.8);
textActiveSource: "#ff950d";
topBanner: "#ededed"
topBannerHover: "#f2f2f2"
lowerBanner: "#ffffff"
accent: "#ff610a";
separator: "#ededed";
playerControlBarFg: "#333333"
expandDelegate: "#ffffff"
isThemeDark: false;
}
},
State {
name: "night"
PropertyChanges {
target: colors_id
text: "#eff0f1"
textInactive: "#bdc3c7"
bg: "#1e1e1e"
bgInactive: "#232629"
bgAlt: "#242424"
bgAltInactive: "#212121"
bgHover: "#303030"
bgHoverText: text
bgHoverInactive: "#3daee9"
bgHoverTextInactive: text
bgFocus: "white"
button: "#31363b"
buttonText: "#eff0f1"
buttonBorder: "#575b5f"
textActiveSource: "#ff950d"
topBanner: "#242424"
topBannerHover: "#272727"
lowerBanner: "#000000"
accent: "#ff8800"
separator: "#2d2d2d"
playerControlBarFg: "#ffffff"
expandDelegate: "#000000"
isThemeDark: true
}
},
State {
name: "system"
PropertyChanges {
target: colors_id
bg: VLC.SystemPalette.base
bgInactive: VLC.SystemPalette.baseInactive
bgAlt: VLC.SystemPalette.alternateBase
bgAltInactive: VLC.SystemPalette.alternateBaseInactive
bgHover: VLC.SystemPalette.highlight
bgHoverText: VLC.SystemPalette.highlightText
bgHoverInactive: VLC.SystemPalette.highlightInactive
bgHoverTextInactive: VLC.SystemPalette.highlightTextInactive
text: VLC.SystemPalette.text
textDisabled: VLC.SystemPalette.textDisabled
textInactive: VLC.SystemPalette.textInactive
button: VLC.SystemPalette.button
buttonText: VLC.SystemPalette.buttonText
buttonBorder: blendColors(button, buttonText, 0.8)
textActiveSource: accent
topBanner: VLC.SystemPalette.window
topBannerHover: VLC.SystemPalette.highlight
lowerBanner: VLC.SystemPalette.base
separator: blendColors(bg, text, .95)
playerControlBarFg: VLC.SystemPalette.text
expandDelegate: bg
isThemeDark: VLC.SystemPalette.isDark
tooltipColor: VLC.SystemPalette.tooltip
tooltipTextColor: VLC.SystemPalette.tooltipText
}
}
]
readonly property color expandDelegate: palette.expandDelegate
}
......@@ -35,11 +35,23 @@ QtObject {
property alias self: vlc_style
readonly property VLCColors colors: VLCColors {}
readonly property SystemPalette theme: SystemPalette {
source: MainCtx.colorScheme.scheme
ctx: MainCtx
}
readonly property VLCColors colors: VLCColors {
palette: vlc_style.theme
}
// When trying to force night/dark theme colors for items,
// this can be used:
readonly property VLCColors nightColors: VLCColors { state: "night" }
readonly property VLCColors nightColors: VLCColors {
palette: SystemPalette {
source: ColorSchemeModel.Night
ctx: MainCtx
}
}
// Sizes
readonly property double margin_xxxsmall: dp(2, scale);
......
/*****************************************************************************
* Copyright (C) 2022 VLC authors and VideoLAN
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef WINDOWSTHEMEPROVIDER_HPP
#define WINDOWSTHEMEPROVIDER_HPP
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <vlc_common.h>
int WindowsThemeProviderOpen(vlc_object_t* p_this);
int SystemPaletteThemeProviderOpen(vlc_object_t* p_this);
#endif // WINDOWSTHEMEPROVIDER_HPP
/*****************************************************************************
* Copyright (C) 2020 VLC authors and VideoLAN
* Copyright (C) 2022 VLC authors and VideoLAN
*
* 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
......@@ -15,70 +15,45 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "systempalette.hpp"
#include <QGuiApplication>
SystemPalette::SystemPalette(QObject* parent)
: QObject(parent)
, m_palette(qApp->palette())
{
connect(qApp, &QGuiApplication::paletteChanged, this, &SystemPalette::paletteChanged);
}
#include "gtk_compat.h"
#define COLOR_GETTER(getter, role) \
QColor SystemPalette::getter() const{ \
return m_palette.color(QPalette::Normal, role); \
} \
QColor SystemPalette::getter##Disabled() const { \
return m_palette.color(QPalette::Disabled, role); \
} \
QColor SystemPalette::getter##Inactive() const { \
return m_palette.color(QPalette::Inactive, role); \
}
namespace gtk {
COLOR_GETTER(text, QPalette::Text)
COLOR_GETTER(textBright, QPalette::BrightText)
COLOR_GETTER(link, QPalette::Link)
COLOR_GETTER(linkVisited, QPalette::LinkVisited)
COLOR_GETTER(base, QPalette::Base)
COLOR_GETTER(alternateBase, QPalette::AlternateBase)
COLOR_GETTER(window, QPalette::Window)
COLOR_GETTER(windowText, QPalette::WindowText)
COLOR_GETTER(button, QPalette::Button)
COLOR_GETTER(buttonText, QPalette::ButtonText)
COLOR_GETTER(highlight, QPalette::Highlight)
COLOR_GETTER(highlightText, QPalette::HighlightedText)
COLOR_GETTER(tooltip, QPalette::ToolTipBase)
COLOR_GETTER(tooltipText, QPalette::ToolTipText)
QColor SystemPalette::light() const
MyInset GtkStyleContextGetPadding(GtkStyleContext* context)
{
return m_palette.color(QPalette::Normal, QPalette::Light);
GtkBorder padding;
gtk_style_context_get_padding(context, gtk_style_context_get_state(context), &padding);
return MyInset(padding);
}
QColor SystemPalette::midlight() const
MyInset GtkStyleContextGetBorder(GtkStyleContext* context)
{
return m_palette.color(QPalette::Normal, QPalette::Midlight);
GtkBorder border;
gtk_style_context_get_border(context, gtk_style_context_get_state(context), &border);
return MyInset(border);
}
QColor SystemPalette::mid() const
MyInset GtkStyleContextGetMargin(GtkStyleContext* context)
{
return m_palette.color(QPalette::Normal, QPalette::Mid);
GtkBorder margin;
gtk_style_context_get_margin(context, gtk_style_context_get_state(context), &margin);
return MyInset(margin);
}
QColor SystemPalette::dark() const
gdouble GdkRBGALightness(GdkRGBA& c1)
{
return m_palette.color(QPalette::Normal, QPalette::Dark);
return 0.2126 * c1.red + 0.7152 * c1.green + 0.0722 * c1.blue;
}
QColor SystemPalette::shadow() const
GdkRGBA GdkRBGABlend(GdkRGBA& c1, GdkRGBA& c2, gdouble blend)
{
return m_palette.color(QPalette::Normal, QPalette::Shadow);
GdkRGBA out;
out.red = c2.red + (c1.red - c2.red) * blend,
out.green = c2.green + (c1.green - c2.green) * blend,
out.blue = c2.blue + (c1.blue - c2.blue) * blend,
out.alpha = c2.alpha + (c1.alpha - c2.alpha) * blend;
return out;
}
bool SystemPalette::isDark() const
{
return base().lightness() < text().lightness();
}
};
/*****************************************************************************
* Copyright (C) 2022 VLC authors and VideoLAN
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef GTK_COMPAT_H
#define GTK_COMPAT_H
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "vlc_common.h"
#include "vlc_picture.h"
#include <algorithm>
#include <gtk/gtk.h>
class VLCPicturePtr
{
public:
VLCPicturePtr()
{
}
VLCPicturePtr(vlc_fourcc_t i_chroma, int i_width, int i_height)
{
m_picture = picture_New(i_chroma, i_width, i_height, 1, 1);
}
VLCPicturePtr(const VLCPicturePtr& o)
: m_picture(o.m_picture)
{
Ref();
}
VLCPicturePtr(VLCPicturePtr&& o)
: m_picture(o.m_picture)
{
o.m_picture = nullptr;
}
VLCPicturePtr& operator=(const VLCPicturePtr& o)
{
Unref();
m_picture = o.m_picture;
Ref();
return *this;
}
VLCPicturePtr& operator=(VLCPicturePtr&& o)
{
Unref();
m_picture = o.m_picture;
o.m_picture = nullptr;
return *this;
}
~VLCPicturePtr()
{
Unref();
}
picture_t* get() const
{
return m_picture;
}
private:
void Ref() {
if (m_picture) {
picture_Hold(m_picture);
}
}
// This function is necessary so that gtk can overload it in
// the case of T = GtkStyleContext.
void Unref() {
if (m_picture)
picture_Release(m_picture);
}
picture_t* m_picture = nullptr;
};
namespace gtk {
//Basic geometry types
gdouble GdkRBGALightness(GdkRGBA& c1);
GdkRGBA GdkRBGABlend(GdkRGBA& c1, GdkRGBA& c2, gdouble blend);
class MySize {
public:
MySize()
{}
MySize(int w, int h)
: m_width(std::max(w, 0))
, m_height(std::max(h, 0))
{}
MySize(const MySize&) = default;
MySize(MySize&&) = default;
MySize& operator=(const MySize&) = default;
MySize& operator=(MySize&&) = default;
inline bool IsEmpty() const { return !m_width || !m_height ; }
inline int width() const { return m_width; }
inline int height() const { return m_height; }
inline void set_width(int width) {
m_width = std::max(width, 0);
}
inline void set_height(int height) {
m_height = std::max(height, 0);
}
private:
int m_width = 0;
int m_height = 0;
};
class MyPoint {
public:
MyPoint(int w, int h)
: x(w)
, y(h)
{}
MyPoint(const MyPoint&) = default;
MyPoint(MyPoint&&) = default;
MyPoint& operator=(const MyPoint&) = default;
MyPoint& operator=(MyPoint&&) = default;
int x = -1;
int y = -1;
};
class MyInset {
public:
MyInset() {}
MyInset(int l,int r,int t,int b)
: m_top(t)
, m_bottom(b)
, m_left(l)
, m_right(r)
{}
MyInset(const GtkBorder& border)
: m_top (border.top)
, m_bottom (border.bottom)
, m_left (border.left)
, m_right (border.right)
{
}
MyInset(const MyInset&) = default;
MyInset(MyInset&&) = default;
MyInset& operator=(const MyInset&) = default;
MyInset& operator=(MyInset&&) = default;
inline int right() const { return m_right; }
inline int left() const { return m_left; }
inline int bottom() const { return m_bottom; }
inline int top() const { return m_top; }
int width() const { return m_left + m_right; }
int height() const { return m_top + m_bottom; }
MyInset operator-() const {
return MyInset(-m_left, -m_right, -m_top, -m_bottom);
}
private:
int m_top = -1;
int m_bottom = -1;
int m_left = -1;
int m_right = -1;
};
class MyRect {
public:
MyRect() {}
MyRect(const MySize& s)
: m_top(0)
, m_left(0)
, m_size(s)
{}
MyRect(const MyPoint& p, const MySize& s)
: m_top(p.y)
, m_left(p.x)
, m_size(s)
{}
MyRect(const MyRect&) = default;
MyRect(MyRect&&) = default;
MyRect& operator=(const MyRect&) = default;
MyRect& operator=(MyRect&&) = default;
inline void Inset(const MyInset& i)
{
m_top += i.top();
m_left += i.left();
set_width(m_size.width() - i.width());
set_height(m_size.height() - i.height());
}
inline int top() const { return m_top; };
inline int left() const { return m_left; };
inline int width() const { return m_size.width(); };
inline int height() const { return m_size.height(); };
inline void set_width(int width) {
m_size.set_width(width);
}
inline void set_height(int height) {
m_size.set_height(height);
}
MySize size() const { return m_size; }
private:
int m_top= 0;
int m_left = 0;
MySize m_size;
};
MyInset GtkStyleContextGetPadding(GtkStyleContext* context);
MyInset GtkStyleContextGetBorder(GtkStyleContext* context);
MyInset GtkStyleContextGetMargin(GtkStyleContext* context);
};
#endif // GTK_COMPAT_H
/*****************************************************************************
* Copyright (C) 2022 VLC authors and VideoLAN
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
// original code from the Chromium project
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "gtk_util.h"
#include "gtk_compat.h"
#include <gdk/gdk.h>
#include <locale.h>
#include <stddef.h>
#include <memory>
namespace gtk {
namespace {
const char kAuraTransientParent[] = "aura-transient-parent";
GtkCssContext AppendCssNodeToStyleContextImpl(
GtkCssContext context,
GType gtype,
const std::string& name,
const std::string& object_name,
const std::vector<std::string>& classes,
GtkStateFlags state,
float scale) {
#if 0 //FIXME GTK4
if (GtkCheckVersion(4)) {
// GTK_TYPE_BOX is used instead of GTK_TYPE_WIDGET because:
// 1. Widgets are abstract and cannot be created directly.
// 2. The widget must be a container type so that it unrefs child widgets
// on destruction.
auto* widget_object = object_name.empty()
? g_object_new(GTK_TYPE_BOX, nullptr)
: g_object_new(GTK_TYPE_BOX, "css-name",
object_name.c_str(), nullptr);
auto widget = TakeGObject(GTK_WIDGET(widget_object));
if (!name.empty())
gtk_widget_set_name(widget, name.c_str());
std::vector<const char*> css_classes;
css_classes.reserve(classes.size() + 1);
for (const auto& css_class : classes)
css_classes.push_back(css_class.c_str());
css_classes.push_back(nullptr);
gtk_widget_set_css_classes(widget, css_classes.data());
gtk_widget_set_state_flags(widget, state, false);
if (context)
gtk_widget_set_parent(widget, context.widget());
gtk_style_context_set_scale(gtk_widget_get_style_context(widget), scale);
return GtkCssContext(widget, context ? context.root() : widget);
} else
#endif
{
GtkWidgetPath* path =
context ? gtk_widget_path_copy(gtk_style_context_get_path(context))
: gtk_widget_path_new();
gtk_widget_path_append_type(path, gtype);
if (!object_name.empty()) {
if (GtkCheckVersion(3, 20))
gtk_widget_path_iter_set_object_name(path, -1, object_name.c_str());
else
gtk_widget_path_iter_add_class(path, -1, object_name.c_str());
}
if (!name.empty())
gtk_widget_path_iter_set_name(path, -1, name.c_str());
for (const auto& css_class : classes)
gtk_widget_path_iter_add_class(path, -1, css_class.c_str());
if (GtkCheckVersion(3, 14))
gtk_widget_path_iter_set_state(path, -1, state);
GtkCssContext child_context(TakeGObject(gtk_style_context_new()));
gtk_style_context_set_path(child_context, path);
if (GtkCheckVersion(3, 14)) {
gtk_style_context_set_state(child_context, state);
} else {
GtkStateFlags child_state = state;
if (context) {
child_state = static_cast<GtkStateFlags>(
child_state | gtk_style_context_get_state(context));
}
gtk_style_context_set_state(child_context, child_state);
}
gtk_style_context_set_scale(child_context, scale);
gtk_style_context_set_parent(child_context, context);
gtk_widget_path_unref(path);
return GtkCssContext(child_context);
}
}
GtkWidget* CreateDummyWindow() {
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_realize(window);
return window;
}
} // namespace
bool GtkCheckVersion(uint32_t major, uint32_t minor, uint32_t micro) {
if (major < gtk_get_major_version())
return true;
else if (major > gtk_get_major_version())
return false;
if (minor < gtk_get_minor_version())
return true;
else if (minor > gtk_get_minor_version())
return false;
return micro <= gtk_get_micro_version();
}
const char* GtkCssMenu() {
return GtkCheckVersion(4) ? "#popover.background.menu #contents"
: "GtkMenu#menu";
}
const char* GtkCssMenuItem() {
return GtkCheckVersion(4) ? "#modelbutton.flat" : "GtkMenuItem#menuitem";
}
const char* GtkCssMenuScrollbar() {
return GtkCheckVersion(4) ? "#scrollbar #range"
: "GtkScrollbar#scrollbar #trough";
}
bool GtkInitFromCommandLine(int* argc, char** argv) {
// Callers should have already called setlocale(LC_ALL, "") and
// setlocale(LC_NUMERIC, "C") by now. Chrome does this in
// service_manager::Main.
assert(strcmp(setlocale(LC_NUMERIC, nullptr), "C") == 0);
// This prevents GTK from calling setlocale(LC_ALL, ""), which potentially
// overwrites the LC_NUMERIC locale to something other than "C".
gtk_disable_setlocale();
return gtk_init_check(argc, &argv);
}
CairoSurface::CairoSurface(VLCPicturePtr& bitmap)
{
picture_t* pic = bitmap.get();
surface_ = (cairo_image_surface_create_for_data(
static_cast<unsigned char*>(pic->p[0].p_pixels),
CAIRO_FORMAT_ARGB32,
pic->format.i_visible_width,
pic->format.i_visible_height,
pic->p[0].i_pitch));
cairo_ = cairo_create(surface_);
}
CairoSurface::CairoSurface(const MySize& size)
: surface_(cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
size.width(),
size.height())),
cairo_(cairo_create(surface_)) {
assert(cairo_surface_status(surface_) == CAIRO_STATUS_SUCCESS);
// Clear the surface.
cairo_save(cairo_);
cairo_set_source_rgba(cairo_, 0, 0, 0, 0);
cairo_set_operator(cairo_, CAIRO_OPERATOR_SOURCE);
cairo_paint(cairo_);
cairo_restore(cairo_);
}
CairoSurface::~CairoSurface() {
// `cairo_destroy` and `cairo_surface_destroy` decrease the reference count on
// `cairo_` and `surface_` objects respectively. The underlying memory is
// freed if the reference count goes to zero. We use ExtractAsDangling() here
// to avoid holding a briefly dangling ptr in case the memory is freed.
cairo_destroy(cairo_);
cairo_surface_destroy(surface_);
}
GdkRGBA CairoSurface::GetAveragePixelValue(bool frame) {
cairo_surface_flush(surface_);
unsigned char* data =cairo_image_surface_get_data(surface_);
int width = cairo_image_surface_get_width(surface_);
int height = cairo_image_surface_get_height(surface_);
int stride = cairo_image_surface_get_stride(surface_);
assert(cairo_image_surface_get_format(surface_) == CAIRO_FORMAT_ARGB32);
long a = 0, r = 0, g = 0, b = 0;
uint64_t max_alpha = 0;
for (int line = 0; line < height; line++)
{
uint64_t* rgbaLine = (uint64_t*)(&data[line*stride]);
for (int i = 0; i < width; i++) {
uint64_t color = rgbaLine[i];
max_alpha = std::max((color >> 24) & 0xFF, max_alpha);
a += (color >> 24) & 0xFF;
r += (color >> 16) & 0xFF;
g += (color >> 8) & 0xFF;
b += (color) & 0xFF;
}
}
GdkRGBA out = {0,0,0,0};
if (a == 0) {
return out;
}
out.red = gdouble(r) / a;
out.green = gdouble(g) / a;
out.blue = gdouble(b) / a;
out.alpha = frame ? (max_alpha / 255.) : gdouble(a) / (width * height * 255);
return out;
}
GtkCssContext::GtkCssContext(GtkWidget* widget, GtkWidget* root)
: widget_(widget), root_(WrapGObject(root)) {
assert(GtkCheckVersion(4));
}
GtkCssContext::GtkCssContext(GtkStyleContext* context)
: context_(WrapGObject(context)) {
assert(!GtkCheckVersion(4));
}
GtkCssContext::GtkCssContext() = default;
GtkCssContext::GtkCssContext(const GtkCssContext&) = default;
GtkCssContext::GtkCssContext(GtkCssContext&&) = default;
GtkCssContext& GtkCssContext::operator=(const GtkCssContext&) = default;
GtkCssContext& GtkCssContext::operator=(GtkCssContext&&) = default;
GtkCssContext::~GtkCssContext() = default;
GtkCssContext::operator GtkStyleContext*() {
if (GtkCheckVersion(4))
return widget_ ? gtk_widget_get_style_context(widget_) : nullptr;
return context_;
}
GtkCssContext GtkCssContext::GetParent() {
if (GtkCheckVersion(4)) {
return GtkCssContext(WrapGObject(gtk_widget_get_parent(widget_)),
root_ == widget_ ? ScopedGObject<GtkWidget>() : root_);
}
return GtkCssContext(WrapGObject(gtk_style_context_get_parent(context_)));
}
GtkWidget* GtkCssContext::widget() {
assert(GtkCheckVersion(4));
return widget_;
}
GtkWidget* GtkCssContext::root() {
assert(GtkCheckVersion(4));
return root_;
}
GtkCssContext AppendCssNodeToStyleContext(GtkCssContext context,
const std::string& css_node) {
enum {
CSS_TYPE,
CSS_NAME,
CSS_OBJECT_NAME,
CSS_CLASS,
CSS_PSEUDOCLASS,
CSS_NONE,
} part_type = CSS_TYPE;
static const struct {
const char* name;
GtkStateFlags state_flag;
} pseudo_classes[] = {
{"active", GTK_STATE_FLAG_ACTIVE},
{"hover", GTK_STATE_FLAG_PRELIGHT},
{"selected", GTK_STATE_FLAG_SELECTED},
{"disabled", GTK_STATE_FLAG_INSENSITIVE},
{"indeterminate", GTK_STATE_FLAG_INCONSISTENT},
{"focus", GTK_STATE_FLAG_FOCUSED},
{"backdrop", GTK_STATE_FLAG_BACKDROP},
{"link", GTK_STATE_FLAG_LINK},
{"visited", GTK_STATE_FLAG_VISITED},
{"checked", GTK_STATE_FLAG_CHECKED},
};
GType gtype = G_TYPE_NONE;
std::string name;
std::string object_name;
std::vector<std::string> classes;
GtkStateFlags state = GTK_STATE_FLAG_NORMAL;
auto it = css_node.cbegin();
//base::StringTokenizer t(css_node, ".:#()");
//t.set_options(base::StringTokenizer::RETURN_DELIMS);
while (it != css_node.cend()) {
auto found = std::find_if(it, css_node.cend(), [](const char c) {
return (c == '.') || (c == ':') || (c == '#') || (c == '(') || (c == ')');
});
std::string token(it, found);
if (token != "")
{
switch (part_type) {
case CSS_NAME:
name = token;
break;
case CSS_OBJECT_NAME:
object_name = token;
break;
case CSS_TYPE: {
if (!GtkCheckVersion(4)) {
gtype = g_type_from_name(token.c_str());
assert(gtype);
}
break;
}
case CSS_CLASS:
classes.push_back(token);
break;
case CSS_PSEUDOCLASS: {
GtkStateFlags state_flag = GTK_STATE_FLAG_NORMAL;
for (const auto& pseudo_class_entry : pseudo_classes) {
if (strcmp(pseudo_class_entry.name, token.c_str()) == 0) {
state_flag = pseudo_class_entry.state_flag;
break;
}
}
state = static_cast<GtkStateFlags>(state | state_flag);
break;
}
case CSS_NONE:
assert(false);
}
}
if (found == css_node.cend())
break;
switch (*found) {
case '(':
part_type = CSS_NAME;
break;
case ')':
part_type = CSS_NONE;
break;
case '#':
part_type = CSS_OBJECT_NAME;
break;
case '.':
part_type = CSS_CLASS;
break;
case ':':
part_type = CSS_PSEUDOCLASS;
break;
default:
assert("unreachable" == 0);
}
it = found + 1;
}
// Always add a "chromium" class so that themes can style chromium
// widgets specially if they want to.
classes.push_back("vlc");
float scale = std::round(GetDeviceScaleFactor());
return AppendCssNodeToStyleContextImpl(context, gtype, name, object_name,
classes, state, scale);
}
GtkCssContext GetStyleContextFromCss(const std::string& css_selector) {
// Prepend a window node to the selector since all widgets must live
// in a window, but we don't want to specify that every time.
auto context = AppendCssNodeToStyleContext({}, "GtkWindow#window.background");
auto it = css_selector.cbegin();
while (it != css_selector.cend())
{
auto found = std::find(it, css_selector.cend(), ' ');
std::string widget_type(it, found);
if (widget_type != "")
context = AppendCssNodeToStyleContext(context, widget_type);
if (found == css_selector.cend())
break;
it = found + 1;
}
return context;
}
GdkRGBA GetBgColorFromStyleContext(GtkCssContext context) {
// Backgrounds are more general than solid colors (eg. gradients),
// but chromium requires us to boil this down to one color. We
// cannot use the background-color here because some themes leave it
// set to a garbage color because a background-image will cover it
// anyway. So we instead render the background into a 24x24 bitmap,
// removing any borders, and hope that we get a good color.
ApplyCssToContext(context,
"* {"
"border-radius: 0px;"
"border-style: none;"
"box-shadow: none;"
"}");
MySize size(24, 24);
CairoSurface surface(size);
RenderBackground(size, surface.cairo(), context);
return surface.GetAveragePixelValue(false);
}
GdkRGBA GtkStyleContextGetColor(GtkStyleContext* context) {
GdkRGBA color;
gtk_style_context_get_color(context, gtk_style_context_get_state(context), &color );
return color;
}
GdkRGBA GetFgColor(const std::string& css_selector) {
return GtkStyleContextGetColor(GetStyleContextFromCss(css_selector));
}
ScopedCssProvider GetCssProvider(const std::string& css) {
auto provider = TakeGObject(gtk_css_provider_new());
gtk_css_provider_load_from_data(provider, css.c_str(), -1, nullptr);
return provider;
}
void ApplyCssProviderToContext(GtkCssContext context,
GtkCssProvider* provider) {
while (context) {
gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider),
G_MAXUINT);
context = context.GetParent();
}
}
void ApplyCssToContext(GtkCssContext context, const std::string& css) {
auto provider = GetCssProvider(css);
ApplyCssProviderToContext(context, provider);
}
void RenderBackground(const MySize& size,
cairo_t* cr,
GtkCssContext context) {
if (!context)
return;
RenderBackground(size, cr, context.GetParent());
gtk_render_background(context, cr, 0, 0, size.width(), size.height());
}
GdkRGBA GetBgColor(const std::string& css_selector) {
return GetBgColorFromStyleContext(GetStyleContextFromCss(css_selector));
}
GdkRGBA GetBorderColor(const std::string& css_selector) {
// Borders have the same issue as backgrounds, due to the
// border-image property.
auto context = GetStyleContextFromCss(css_selector);
MySize size(24, 24);
CairoSurface surface(size);
gtk_render_frame(context, surface.cairo(), 0, 0, size.width(), size.height());
return surface.GetAveragePixelValue(true);
}
GdkRGBA GetSelectionBgColor(const std::string& css_selector) {
auto context = GetStyleContextFromCss(css_selector);
return GetBgColorFromStyleContext(context);
}
bool ContextHasClass(GtkCssContext context, const std::string& style_class) {
bool has_class = gtk_style_context_has_class(context, style_class.c_str());
if (!GtkCheckVersion(4)) {
has_class |= gtk_widget_path_iter_has_class(
gtk_style_context_get_path(context), -1, style_class.c_str());
}
return has_class;
}
GdkRGBA GetSeparatorColor(const std::string& css_selector) {
auto context = GetStyleContextFromCss(css_selector);
bool horizontal = ContextHasClass(context, "horizontal");
int w = 1, h = 1;
if (GtkCheckVersion(4)) {
auto size = GetSeparatorSize(horizontal);
w = size.width();
h = size.height();
} else {
gtk_style_context_get(context, gtk_style_context_get_state(context), "min-width", &w, "min-height", &h, nullptr);
}
MyInset border = GtkStyleContextGetBorder(context);
MyInset padding = GtkStyleContextGetPadding(context);
w += border.left() + padding.left() + padding.right() + border.right();
h += border.top() + padding.top() + padding.bottom() + border.bottom();
if (horizontal) {
w = 24;
h = std::max(h, 1);
} else {
assert(ContextHasClass(context, "vertical"));
h = 24;
w = std::max(w, 1);
}
CairoSurface surface(MySize(w, h));
gtk_render_background(context, surface.cairo(), 0, 0, w, h);
gtk_render_frame(context, surface.cairo(), 0, 0, w, h);
return surface.GetAveragePixelValue(false);
}
std::string GetGtkSettingsStringProperty(GtkSettings* settings,
const gchar* prop_name) {
GValue layout = G_VALUE_INIT;
g_value_init(&layout, G_TYPE_STRING);
g_object_get_property(G_OBJECT(settings), prop_name, &layout);
assert(G_VALUE_HOLDS_STRING(&layout));
std::string prop_value(g_value_get_string(&layout));
g_value_unset(&layout);
return prop_value;
}
GtkIconTheme* GetDefaultIconTheme() {
#if 0 //FIXME GTK4
return GtkCheckVersion(4)
? gtk_icon_theme_get_for_display(gdk_display_get_default())
: gtk_icon_theme_get_default();
#endif
return gtk_icon_theme_get_default();
}
void GtkWindowDestroy(GtkWidget* widget) {
#if 0 //FIXME GTK4
if (GtkCheckVersion(4))
gtk_window_destroy(GTK_WINDOW(widget));
#endif
gtk_widget_destroy(widget);
}
GtkWidget* GetDummyWindow() {
static GtkWidget* window = CreateDummyWindow();
return window;
}
MySize GetSeparatorSize(bool horizontal) {
auto widget = TakeGObject(gtk_separator_new(
horizontal ? GTK_ORIENTATION_HORIZONTAL : GTK_ORIENTATION_VERTICAL));
GtkRequisition natural_size;
gtk_widget_get_preferred_size(widget, nullptr, &natural_size);
return {natural_size.width, natural_size.height};
}
static float g_deviceScaleFactor = 1.0f;
void SetDeviceScaleFactor(float scaleFactor) {
g_deviceScaleFactor = scaleFactor;
}
float GetDeviceScaleFactor() {
return g_deviceScaleFactor;
}
} // namespace gtk
/*****************************************************************************
* Copyright (C) 2022 VLC authors and VideoLAN
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
// original code from the Chromium project
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_GTK_GTK_UTIL_H_
#define UI_GTK_GTK_UTIL_H_
#include <string>
#include <vector>
#include <gdk/gdk.h>
#include <gio/gio.h>
#include <gtk/gtk.h>
#include "gtk_compat.h"
#include "scoped_gobject.h"
#include "vlc_picture.h"
namespace gtk {
bool GtkCheckVersion(uint32_t major, uint32_t minor = 0, uint32_t micro = 0);
const char* GtkCssMenu();
const char* GtkCssMenuItem();
const char* GtkCssMenuScrollbar();
bool GtkInitFromCommandLine(int* argc, char** argv);
// Sets |dialog| as transient for |parent|, which will keep it on top and center
// it above |parent|. Do nothing if |parent| is nullptr.
//void SetGtkTransientForAura(GtkWidget* dialog, aura::Window* parent);
// Gets the transient parent aura window for |dialog|.
//aura::Window* GetAuraTransientParent(GtkWidget* dialog);
// Clears the transient parent for |dialog|.
//void ClearAuraTransientParent(GtkWidget* dialog, aura::Window* parent);
// Parses |button_string| into |leading_buttons| and
// |trailing_buttons|. The string is of the format
// "<button>*:<button*>", for example, "close:minimize:maximize".
// This format is used by GTK settings and gsettings.
//void ParseButtonLayout(const std::string& button_string,
// std::vector<views::FrameButton>* leading_buttons,
// std::vector<views::FrameButton>* trailing_buttons);
class CairoSurface {
public:
// Attaches a cairo surface to an SkBitmap so that GTK can render
// into it. |bitmap| must outlive this CairoSurface.
explicit CairoSurface(VLCPicturePtr& bitmap);
// Creates a new cairo surface with the given size. The memory for
// this surface is deallocated when this CairoSurface is destroyed.
explicit CairoSurface(const MySize& size);
~CairoSurface();
// Get the drawing context for GTK to use.
cairo_t* cairo() { return cairo_; }
// Returns the average of all pixels in the surface. If |frame| is
// true, the resulting alpha will be the average alpha, otherwise it
// will be the max alpha across all pixels.
GdkRGBA GetAveragePixelValue(bool frame);
private:
cairo_surface_t* surface_;
cairo_t* cairo_;
};
class GtkCssContext {
public:
GtkCssContext();
GtkCssContext(const GtkCssContext&);
GtkCssContext(GtkCssContext&&);
GtkCssContext& operator=(const GtkCssContext&);
GtkCssContext& operator=(GtkCssContext&&);
~GtkCssContext();
// GTK3 constructor.
explicit GtkCssContext(GtkStyleContext* context);
// GTK4 constructor.
GtkCssContext(GtkWidget* widget, GtkWidget* root);
// As a convenience, allow using a GtkCssContext as a gtk_style_context()
// to avoid repeated use of an explicit getter.
// NOLINTNEXTLINE(google-explicit-constructor)
operator GtkStyleContext*();
GtkCssContext GetParent();
// Only available on GTK4.
GtkWidget* widget();
GtkWidget* root();
private:
// GTK3 state.
ScopedGObject<GtkStyleContext> context_;
// GTK4 state.
// GTK widgets own their children, so instead of keeping a reference to the
// widget directly, keep a reference to the root widget.
GtkWidget* widget_ = nullptr;
ScopedGObject<GtkWidget> root_;
};
using ScopedCssProvider = ScopedGObject<GtkCssProvider>;
} // namespace gtk
// Template override cannot be in the gtk namespace.
template <>
inline void ScopedGObject<GtkStyleContext>::Unref() {
// Versions of GTK earlier than 3.15.4 had a bug where a g_assert
// would be triggered when trying to free a GtkStyleContext that had
// a parent whose only reference was the child context in question.
// This is a hack to work around that case. See GTK commit
// "gtkstylecontext: Don't try to emit a signal when finalizing".
GtkStyleContext* context = obj_;
while (context) {
GtkStyleContext* parent = gtk_style_context_get_parent(context);
if (parent && G_OBJECT(context)->ref_count == 1 &&
!gtk::GtkCheckVersion(3, 15, 4)) {
g_object_ref(parent);
gtk_style_context_set_parent(context, nullptr);
g_object_unref(context);
} else {
g_object_unref(context);
return;
}
context = parent;
}
}
namespace gtk {
// If |context| is nullptr, creates a new top-level style context
// specified by parsing |css_node|. Otherwise, creates the child
// context with |context| as the parent.
GtkCssContext AppendCssNodeToStyleContext(GtkCssContext context,
const std::string& css_node);
// Parses |css_selector| into a StyleContext. The format is a
// sequence of whitespace-separated objects. Each object may have at
// most one object name at the beginning of the string, and any number
// of '.'-prefixed classes and ':'-prefixed pseudoclasses. An example
// is "GtkButton.button.suggested-action:hover:active". The caller
// must g_object_unref() the returned context.
GtkCssContext GetStyleContextFromCss(const std::string& css_selector);
GdkRGBA GtkStyleContextGetColor(GtkStyleContext* context);
GdkRGBA GetBgColorFromStyleContext(GtkCssContext context);
// Overrides properties on |context| and all its parents with those
// provided by |css|.
void ApplyCssToContext(GtkCssContext context, const std::string& css);
// Get the 'color' property from the style context created by
// GetStyleContextFromCss(|css_selector|).
GdkRGBA GetFgColor(const std::string& css_selector);
ScopedCssProvider GetCssProvider(const std::string& css);
// Renders the backgrounds of all ancestors of |context|, then renders
// the background for |context| itself.
void RenderBackground(const MySize& size,
cairo_t* cr,
GtkCssContext context);
// Renders a background from the style context created by
// GetStyleContextFromCss(|css_selector|) into a 24x24 bitmap and
// returns the average color.
GdkRGBA GetBgColor(const std::string& css_selector);
// Renders the border from the style context created by
// GetStyleContextFromCss(|css_selector|) into a 24x24 bitmap and
// returns the average color.
GdkRGBA GetBorderColor(const std::string& css_selector);
// On Gtk3.20 or later, behaves like GetBgColor. Otherwise, returns
// the background-color property.
GdkRGBA GetSelectionBgColor(const std::string& css_selector);
// Get the color of the GtkSeparator specified by |css_selector|.
GdkRGBA GetSeparatorColor(const std::string& css_selector);
// Get a GtkSettings property as a C++ string.
std::string GetGtkSettingsStringProperty(GtkSettings* settings,
const gchar* prop_name);
GtkIconTheme* GetDefaultIconTheme();
void GtkWindowDestroy(GtkWidget* widget);
GtkWidget* GetDummyWindow();
MySize GetSeparatorSize(bool horizontal);
void SetDeviceScaleFactor(float scaleFactor);
float GetDeviceScaleFactor();
} // namespace gtk
#endif // UI_GTK_GTK_UTIL_H_
/*****************************************************************************
* Copyright (C) 2022 VLC authors and VideoLAN
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_plugin.h>
#include "../qtthemeprovider.hpp"
#include <mutex>
#include "nav_button_provider_gtk.h"
#include "gtk_util.h"
using namespace gtk;
class GtkPrivateThemeProvider {
public:
bool windowMaximized = false;
bool windowActive = false;
int bannerHeight = -1;
NavButtonProviderGtk navButtons;
//Metrics
int interNavButtonSpacing = 0;
int csdFrameMarginLeft = 0;
int csdFrameMarginRight = 0;
int csdFrameMarginTop = 0;
int csdFrameMarginBottom = 0;
};
static bool isThemeDark( vlc_qt_theme_provider_t*)
{
GdkRGBA colBg = GetBgColor("");
GdkRGBA colText = GetFgColor("GtkLabel#label");
return GdkRBGALightness(colBg) < GdkRBGALightness(colText);
}
static void setGtkColor(vlc_qt_theme_provider_t* obj, void* qColor, const GdkRGBA& gColor)
{
obj->setColorF(qColor, gColor.red, gColor.green, gColor.blue, gColor.alpha);
}
template<typename Getter>
static void setGtkColorSet(vlc_qt_theme_provider_t* obj, void* base, void* disabled, void* inactive, Getter& getter, std::string selector)
{
if (base)
setGtkColor(obj, base, getter(selector));
if (disabled)
setGtkColor(obj, disabled, getter(selector + ":disabled"));
if (inactive)
setGtkColor(obj, inactive, getter(selector + ":backdrop"));
}
picture_t* getThemeImage(vlc_qt_theme_provider_t* obj, vlc_qt_theme_image_type type, const vlc_qt_theme_image_setting* settings)
{
auto sys = static_cast<GtkPrivateThemeProvider*>(obj->p_sys);
if (type == VLC_QT_THEME_IMAGE_TYPE_CSD_BUTTON)
{
SetDeviceScaleFactor(settings->windowScaleFactor);
bool windowMaximized = settings->u.csdButton.maximized;
bool windowActive = settings->u.csdButton.active;
int bannerHeight = settings->u.csdButton.bannerHeight;
vlc_qt_theme_csd_button_state state = settings->u.csdButton.state;
vlc_qt_theme_csd_button_type buttonType = settings->u.csdButton.buttonType;
if (buttonType == VLC_QT_THEME_BUTTON_MAXIMIZE && windowMaximized)
buttonType = VLC_QT_THEME_BUTTON_RESTORE;
else if (buttonType == VLC_QT_THEME_BUTTON_RESTORE && !windowMaximized)
buttonType = VLC_QT_THEME_BUTTON_MAXIMIZE;
if (windowActive != sys->windowActive
|| windowMaximized != sys->windowMaximized
|| bannerHeight != sys->bannerHeight)
{
sys->navButtons.RedrawImages(bannerHeight, windowMaximized, windowActive);
sys->bannerHeight = bannerHeight;
sys->windowMaximized = windowMaximized;
sys->windowActive = windowActive;
sys->interNavButtonSpacing = sys->navButtons.GetInterNavButtonSpacing();
MyInset frameMargin = sys->navButtons.GetTopAreaSpacing();
sys->csdFrameMarginLeft = frameMargin.left();
sys->csdFrameMarginRight = frameMargin.right();
sys->csdFrameMarginTop = frameMargin.top();
sys->csdFrameMarginBottom = frameMargin.bottom();
if (obj->metricsUpdated)
obj->metricsUpdated(obj, VLC_QT_THEME_IMAGE_TYPE_CSD_BUTTON, obj->metricsUpdatedData);
}
picture_t* pic = sys->navButtons.GetImage(buttonType, state).get();
if (pic)
picture_Hold(pic);
return pic;
}
return nullptr;
}
static bool getThemeMetrics(vlc_qt_theme_provider_t* obj, vlc_qt_theme_image_type type, struct vlc_qt_theme_metrics* metrics)
{
auto sys = static_cast<GtkPrivateThemeProvider*>(obj->p_sys);
if (type != VLC_QT_THEME_IMAGE_TYPE_CSD_BUTTON)
return false;
metrics->u.csd.interNavButtonSpacing = sys->interNavButtonSpacing;
metrics->u.csd.csdFrameMarginLeft = sys->csdFrameMarginLeft;
metrics->u.csd.csdFrameMarginRight = sys->csdFrameMarginRight;
metrics->u.csd.csdFrameMarginTop = sys->csdFrameMarginTop;
metrics->u.csd.csdFrameMarginBottom = sys->csdFrameMarginBottom;
return true;
}
bool supportThemeImage(vlc_qt_theme_provider_t*, vlc_qt_theme_image_type type)
{
if (type == VLC_QT_THEME_IMAGE_TYPE_CSD_BUTTON)
return true;
return false;
}
static void updatePalette(vlc_qt_theme_provider_t* obj, struct vlc_qt_palette_t* p)
{
GdkRGBA bg = GetBgColor("GtkTreeView#treeview.view GtkTreeView#treeview.view.cell");
GdkRGBA text = GetFgColor("GtkLabel#label");
setGtkColorSet(obj, p->bg, nullptr, p->bgInactive, GetBgColor, "GtkTreeView#treeview.view GtkTreeView#treeview.view.cell");
setGtkColorSet(obj, p->bgAlt, nullptr, p->bgAltInactive, GetBgColor, "");
setGtkColorSet(obj, p->text, p->textDisabled, p->textInactive, GetFgColor, "GtkLabel#label");
setGtkColorSet(obj, p->bgHover, nullptr, p->bgHoverInactive, GetSelectionBgColor, "GtkLabel#label #selection");
setGtkColorSet(obj, p->bgHoverText, nullptr, p->bgHoverTextInactive, GetFgColor, "GtkLabel#setGtkColorSet");
setGtkColorSet(obj, p->button, nullptr, nullptr, GetBgColor, "GtkButton#button");
setGtkColorSet(obj, p->buttonText, nullptr, nullptr, GetFgColor, "GtkButton#button GtkLabel#label");
setGtkColorSet(obj, p->buttonBorder, nullptr, nullptr, GetBorderColor, "GtkButton#button");
setGtkColor(obj, p->topBanner, GetBgColor("#headerbar.header-bar.titlebar"));
setGtkColor(obj, p->lowerBanner, GetBgColor(""));
setGtkColor(obj, p->expandDelegate, bg);
setGtkColor(obj, p->separator, GdkRBGABlend(bg, text, .95));
setGtkColor(obj, p->playerControlBarFg, text);
const auto tooltip_context = AppendCssNodeToStyleContext({}, "#tooltip.background");
setGtkColor(obj, p->tooltipColor, GetBgColorFromStyleContext(tooltip_context));
setGtkColor(obj, p->tooltipTextColor, GtkStyleContextGetColor(AppendCssNodeToStyleContext(tooltip_context, "GtkLabel#label")));
}
static void Close(vlc_qt_theme_provider_t* obj)
{
auto sys = static_cast<GtkPrivateThemeProvider*>(obj->p_sys);
delete sys;
}
static int Open(vlc_object_t* p_this)
{
vlc_qt_theme_provider_t* obj = (vlc_qt_theme_provider_t*)p_this;
std::once_flag flag;
std::call_once(flag, [](){
int argc = 0;
char** argv = nullptr;
gtk_init(&argc, &argv);
gdk_init(&argc, &argv);
//register types needed by g_type_from_name
g_type_class_unref(g_type_class_ref(gtk_button_get_type()));
g_type_class_unref(g_type_class_ref(gtk_entry_get_type()));
g_type_class_unref(g_type_class_ref(gtk_frame_get_type()));
g_type_class_unref(g_type_class_ref(gtk_header_bar_get_type()));
g_type_class_unref(g_type_class_ref(gtk_image_get_type()));
g_type_class_unref(g_type_class_ref(gtk_info_bar_get_type()));
g_type_class_unref(g_type_class_ref(gtk_label_get_type()));
g_type_class_unref(g_type_class_ref(gtk_menu_get_type()));
g_type_class_unref(g_type_class_ref(gtk_menu_bar_get_type()));
g_type_class_unref(g_type_class_ref(gtk_menu_item_get_type()));
g_type_class_unref(g_type_class_ref(gtk_range_get_type()));
g_type_class_unref(g_type_class_ref(gtk_scrollbar_get_type()));
g_type_class_unref(g_type_class_ref(gtk_scrolled_window_get_type()));
g_type_class_unref(g_type_class_ref(gtk_separator_get_type()));
g_type_class_unref(g_type_class_ref(gtk_spinner_get_type()));
g_type_class_unref(g_type_class_ref(gtk_text_view_get_type()));
g_type_class_unref(g_type_class_ref(gtk_toggle_button_get_type()));
g_type_class_unref(g_type_class_ref(gtk_tree_view_get_type()));
g_type_class_unref(g_type_class_ref(gtk_window_get_type()));
g_type_class_unref(g_type_class_ref(gtk_combo_box_text_get_type()));
g_type_class_unref(g_type_class_ref(gtk_cell_view_get_type()));
g_type_class_unref(g_type_class_ref(gtk_scale_get_type()));
});
auto sys = new (std::nothrow) GtkPrivateThemeProvider();
if (!sys)
return VLC_EGENERIC;
obj->p_sys = sys;
obj->close = Close;
obj->isThemeDark = isThemeDark;
obj->updatePalette = updatePalette;
obj->supportThemeImage = supportThemeImage;
obj->getThemeImage = getThemeImage;
obj->getThemeMetrics = getThemeMetrics;
return VLC_SUCCESS;
}
vlc_module_begin()
add_shortcut("qt-themeprovider-gtk")
set_description( "Qt GTK system theme" )
set_capability("qt theme provider", 0)
set_callback(Open)
vlc_module_end()
/*****************************************************************************
* Copyright (C) 2022 VLC authors and VideoLAN
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
// original code from the Chromium project
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <cmath>
#include <memory>
#include "scoped_gobject.h"
#include "gtk_util.h"
#include "gtk_compat.h"
#include "nav_button_provider_gtk.h"
namespace gtk {
namespace {
struct NavButtonIcon {
// Used on Gtk3.
ScopedGObject<GdkPixbuf> pixbuf;
#if 0 //GTK4
// Used on Gtk4.
ScopedGObject<GdkTexture> texture;
#endif
};
// gtkheaderbar.c uses GTK_ICON_SIZE_MENU, which is 16px.
const int kNavButtonIconSize = 16;
// Specified in GtkHeaderBar spec.
const int kHeaderSpacing = 6;
const char* ButtonStyleClassFromButtonType(
vlc_qt_theme_csd_button_type type) {
switch (type) {
case VLC_QT_THEME_BUTTON_MINIMIZE:
return "minimize";
case VLC_QT_THEME_BUTTON_MAXIMIZE:
case VLC_QT_THEME_BUTTON_RESTORE:
return "maximize";
case VLC_QT_THEME_BUTTON_CLOSE:
return "close";
default:
assert("unreachable" == 0);
return "";
}
}
GtkStateFlags GtkStateFlagsFromButtonState(vlc_qt_theme_csd_button_state state) {
switch (state) {
case VLC_QT_THEME_BUTTON_STATE_NORMAL:
return GTK_STATE_FLAG_NORMAL;
case VLC_QT_THEME_BUTTON_STATE_HOVERED:
return GTK_STATE_FLAG_PRELIGHT;
case VLC_QT_THEME_BUTTON_STATE_PRESSED:
return static_cast<GtkStateFlags>(GTK_STATE_FLAG_PRELIGHT |
GTK_STATE_FLAG_ACTIVE);
case VLC_QT_THEME_BUTTON_STATE_DISABLED:
return GTK_STATE_FLAG_INSENSITIVE;
default:
assert("unreachable" == 0);
return GTK_STATE_FLAG_NORMAL;
}
}
const char* IconNameFromButtonType(vlc_qt_theme_csd_button_type type) {
switch (type) {
case VLC_QT_THEME_BUTTON_MINIMIZE:
return "window-minimize-symbolic";
case VLC_QT_THEME_BUTTON_MAXIMIZE:
return "window-maximize-symbolic";
case VLC_QT_THEME_BUTTON_RESTORE:
return "window-restore-symbolic";
case VLC_QT_THEME_BUTTON_CLOSE:
return "window-close-symbolic";
default:
assert("unreachable" == 0);
return "";
}
}
MySize LoadNavButtonIcon(
vlc_qt_theme_csd_button_type type,
GtkStyleContext* button_context,
int scale,
NavButtonIcon* icon = nullptr) {
const char* icon_name = IconNameFromButtonType(type);
#if 0 //GTK4
if (!GtkCheckVersion(4)) {
#endif
auto icon_info = TakeGObject(gtk_icon_theme_lookup_icon_for_scale(
GetDefaultIconTheme(), icon_name, kNavButtonIconSize, scale,
static_cast<GtkIconLookupFlags>(GTK_ICON_LOOKUP_USE_BUILTIN |
GTK_ICON_LOOKUP_GENERIC_FALLBACK
)));
auto icon_pixbuf = TakeGObject(gtk_icon_info_load_symbolic_for_context(
icon_info, button_context, nullptr, nullptr));
MySize size{gdk_pixbuf_get_width(icon_pixbuf),
gdk_pixbuf_get_height(icon_pixbuf)};
if (icon)
icon->pixbuf = std::move(icon_pixbuf);
return size;
#if 0 //GTK4
}
auto icon_paintable = Gtk4IconThemeLookupIcon(
GetDefaultIconTheme(), icon_name, nullptr, kNavButtonIconSize, scale,
GTK_TEXT_DIR_NONE, static_cast<GtkIconLookupFlags>(0));
auto* paintable =
GlibCast<GdkPaintable>(icon_paintable.get(), gdk_paintable_get_type());
int width = scale * gdk_paintable_get_intrinsic_width(paintable);
int height = scale * gdk_paintable_get_intrinsic_height(paintable);
if (icon) {
auto* snapshot = gtk_snapshot_new();
gdk_paintable_snapshot(paintable, snapshot, width, height);
auto* node = gtk_snapshot_free_to_node(snapshot);
GdkTexture* texture = GetTextureFromRenderNode(node);
size_t nbytes = width * height * sizeof(SkColor);
SkColor* pixels = reinterpret_cast<SkColor*>(g_malloc(nbytes));
size_t stride = sizeof(SkColor) * width;
gdk_texture_download(texture, reinterpret_cast<guchar*>(pixels), stride);
SkColor fg = GtkStyleContextGetColor(button_context);
for (int i = 0; i < width * height; ++i)
pixels[i] = SkColorSetA(fg, SkColorGetA(pixels[i]));
icon->texture = TakeGObject(
gdk_memory_texture_new(width, height, GDK_MEMORY_B8G8R8A8,
g_bytes_new_take(pixels, nbytes), stride));
gsk_render_node_unref(node);
}
return {width, height};
#endif
}
MySize GetMinimumWidgetSize(MySize content_size,
GtkStyleContext* content_context,
GtkCssContext widget_context) {
MyRect widget_rect = MyRect({0,0},content_size);
if (content_context) {
widget_rect.Inset(-GtkStyleContextGetMargin(content_context));
}
int min_width = 0;
int min_height = 0;
// On GTK3, get the min size from the CSS directly.
if (GtkCheckVersion(3, 20) && !GtkCheckVersion(4)) {
gtk_style_context_get(widget_context, gtk_style_context_get_state(widget_context),
"min-width", &min_width,
"min-height", &min_height,
nullptr);
widget_rect.set_width(std::max(widget_rect.width(), min_width));
widget_rect.set_height(std::max(widget_rect.height(), min_height));
}
widget_rect.Inset(-GtkStyleContextGetPadding(widget_context));
widget_rect.Inset(-GtkStyleContextGetBorder(widget_context));
#if 0 //GTK4
// On GTK4, the CSS properties are hidden, so compute the min size indirectly,
// which will include the border, margin, and padding. We can't take this
// codepath on GTK3 since we only have a widget available in GTK4.
if (GtkCheckVersion(4)) {
gtk_widget_measure(widget_context.widget(), GTK_ORIENTATION_HORIZONTAL, -1,
&min_width, nullptr, nullptr, nullptr);
gtk_widget_measure(widget_context.widget(), GTK_ORIENTATION_VERTICAL, -1,
&min_height, nullptr, nullptr, nullptr);
// The returned "minimum size" is the drawn size of the widget, which
// doesn't include the margin. However, GTK includes this size in its
// calculation. So remove the margin, recompute the min size, then add it
// back.
auto margin = GtkStyleContextGetMargin(widget_context);
widget_rect.Inset(-margin);
widget_rect.set_width(std::max(widget_rect.width(), min_width));
widget_rect.set_height(std::max(widget_rect.height(), min_height));
widget_rect.Inset(margin);
}
#endif
return widget_rect.size();
}
GtkCssContext CreateHeaderContext(bool maximized) {
std::string window_selector = "GtkWindow#window.background.csd";
if (maximized)
window_selector += ".maximized";
return AppendCssNodeToStyleContext(
AppendCssNodeToStyleContext({}, window_selector),
"GtkHeaderBar#headerbar.header-bar.titlebar");
}
GtkCssContext CreateWindowControlsContext(bool maximized) {
return AppendCssNodeToStyleContext(CreateHeaderContext(maximized),
"#windowcontrols");
}
void CalculateUnscaledButtonSize(
vlc_qt_theme_csd_button_type type,
bool maximized,
MySize* button_size,
MyInset* button_margin) {
// views::ImageButton expects the images for each state to be of the
// same size, but GTK can, in general, use a differnetly-sized
// button for each state. For this reason, render buttons for all
// states at the size of a GTK_STATE_FLAG_NORMAL button.
auto button_context = AppendCssNodeToStyleContext(
CreateWindowControlsContext(maximized),
"GtkButton#button.titlebutton." +
std::string(ButtonStyleClassFromButtonType(type)));
auto icon_size = LoadNavButtonIcon(type, button_context, 1);
auto image_context =
AppendCssNodeToStyleContext(button_context, "GtkImage#image");
MySize image_size =
GetMinimumWidgetSize(icon_size, nullptr, image_context);
*button_size =
GetMinimumWidgetSize(image_size, image_context, button_context);
*button_margin = GtkStyleContextGetMargin(button_context);
}
class NavButtonImageSource {
public:
NavButtonImageSource(vlc_qt_theme_csd_button_type type,
vlc_qt_theme_csd_button_state state,
bool maximized,
bool active,
MySize button_size)
: type_(type)
, state_(state)
, maximized_(maximized)
, active_(active)
, button_size_(button_size)
{}
~NavButtonImageSource() = default;
VLCPicturePtr GetImageForScale(float scale) {
// gfx::ImageSkia kindly caches the result of this function, so
// RenderNavButton() is called at most once for each needed scale
// factor. Additionally, buttons in the HOVERED or PRESSED states
// are not actually rendered until they are needed.
if (button_size_.IsEmpty())
return {};
auto button_context =
AppendCssNodeToStyleContext(CreateWindowControlsContext(maximized_),
std::string("GtkButton#button.titlebutton.") + ButtonStyleClassFromButtonType(type_));
GtkStateFlags button_state = GtkStateFlagsFromButtonState(state_);
if (!active_) {
button_state =
static_cast<GtkStateFlags>(button_state | GTK_STATE_FLAG_BACKDROP);
}
gtk_style_context_set_state(button_context, button_state);
// Gtk header bars usually have the same height in both maximized and
// restored windows. But chrome's tabstrip background has a smaller height
// when maximized. To prevent buttons from clipping outside of this region,
// they are scaled down. However, this is problematic for themes that do
// not expect this case and use bitmaps for frame buttons (like the Breeze
// theme). When the background-size is set to auto, the background bitmap
// is not scaled for the (unexpected) smaller button size, and the button's
// edges appear cut off. To fix this, manually set the background to scale
// to the button size when it would have clipped.
//
// GTK's "contain" is unlike CSS's "contain". In CSS, the image would only
// be downsized when it would have clipped. In GTK, the image is always
// scaled to fit the drawing region (preserving aspect ratio). Only add
// "contain" if clipping would occur.
int bg_width = 0;
int bg_height = 0;
#if 0 //GTK4
if (GtkCheckVersion(4)) {
auto* snapshot = gtk_snapshot_new();
gtk_snapshot_render_background(snapshot, button_context, 0, 0,
button_size_.width(),
button_size_.height());
if (auto* node = gtk_snapshot_free_to_node(snapshot)) {
if (GdkTexture* texture = GetTextureFromRenderNode(node)) {
bg_width = gdk_texture_get_width(texture);
bg_height = gdk_texture_get_height(texture);
}
gsk_render_node_unref(node);
}
} else
#endif
{
cairo_pattern_t* cr_pattern = nullptr;
cairo_surface_t* cr_surface = nullptr;
gtk_style_context_get(button_context, gtk_style_context_get_state(button_context),
"background-image", &cr_pattern,
nullptr);
if (cr_pattern) {
cairo_pattern_get_surface(cr_pattern, &cr_surface);
if (cr_surface &&
cairo_surface_get_type(cr_surface) == CAIRO_SURFACE_TYPE_IMAGE) {
bg_width = cairo_image_surface_get_width(cr_surface);
bg_height = cairo_image_surface_get_height(cr_surface);
}
cairo_pattern_destroy(cr_pattern);
}
}
if (bg_width > button_size_.width() || bg_height > button_size_.height()) {
ApplyCssToContext(button_context,
".titlebutton { background-size: contain; }");
}
// Gtk doesn't support fractional scale factors, but chrome does.
// Rendering the button background and border at a fractional
// scale factor is easy, since we can adjust the cairo context
// transform. But the icon is loaded from a pixbuf, so we pick
// the next-highest integer scale and manually downsize.
int pixbuf_scale = scale == static_cast<int>(scale) ? scale : scale + 1;
auto icon_context = AppendCssNodeToStyleContext(button_context, "GtkImage#image");
NavButtonIcon icon;
auto icon_size =
LoadNavButtonIcon(type_, icon_context, pixbuf_scale, &icon);
VLCPicturePtr bitmap(
VLC_CODEC_ARGB,
scale * button_size_.width(),
scale * button_size_.height());
if (!bitmap.get())
return {};
CairoSurface surface(bitmap);
cairo_t* cr = surface.cairo();
cairo_save(cr);
cairo_set_source_rgba(cr, 0, 0, 0, 0);
cairo_set_operator(cr, CAIRO_OPERATOR_SOURCE);
cairo_paint(cr);
cairo_restore(cr);
cairo_save(cr);
cairo_scale(cr, scale, scale);
if (GtkCheckVersion(3, 11, 3) ||
(button_state & (GTK_STATE_FLAG_PRELIGHT | GTK_STATE_FLAG_ACTIVE))) {
gtk_render_background(button_context, cr, 0, 0, button_size_.width(),
button_size_.height());
gtk_render_frame(button_context, cr, 0, 0, button_size_.width(),
button_size_.height());
}
cairo_restore(cr);
cairo_save(cr);
float pixbuf_extra_scale = scale / pixbuf_scale;
cairo_scale(cr, pixbuf_extra_scale, pixbuf_extra_scale);
gtk_render_icon(icon_context, cr, icon.pixbuf,
((pixbuf_scale * button_size_.width() - icon_size.width()) / 2),
((pixbuf_scale * button_size_.height() - icon_size.height()) / 2));
cairo_restore(cr);
return bitmap;
}
bool HasRepresentationAtAllScales() const { return true; }
private:
vlc_qt_theme_csd_button_type type_;
vlc_qt_theme_csd_button_state state_;
bool maximized_;
bool active_;
MySize button_size_;
};
} // namespace
NavButtonProviderGtk::NavButtonProviderGtk() = default;
NavButtonProviderGtk::~NavButtonProviderGtk() = default;
void NavButtonProviderGtk::RedrawImages(int top_area_height,
bool maximized,
bool active) {
auto header_context = CreateHeaderContext(maximized);
MyInset header_padding = GtkStyleContextGetPadding(header_context);
double scale = 1.0f;
std::map<vlc_qt_theme_csd_button_type, MySize>
button_sizes;
std::map<vlc_qt_theme_csd_button_type, MyInset>
button_margins;
std::vector<vlc_qt_theme_csd_button_type> display_types{
VLC_QT_THEME_BUTTON_MINIMIZE,
maximized ? VLC_QT_THEME_BUTTON_RESTORE
: VLC_QT_THEME_BUTTON_MAXIMIZE,
VLC_QT_THEME_BUTTON_CLOSE,
};
for (auto type : display_types) {
CalculateUnscaledButtonSize(type, maximized, &button_sizes[type],
&button_margins[type]);
int button_unconstrained_height = button_sizes[type].height() +
button_margins[type].top() +
button_margins[type].bottom();
int needed_height = header_padding.top() + button_unconstrained_height +
header_padding.bottom();
if (needed_height > top_area_height)
scale = std::min(scale, static_cast<double>(top_area_height) / needed_height);
}
top_area_spacing_ = MyInset(std::round(scale * header_padding.top()),
std::round(scale * header_padding.left()),
std::round(scale * header_padding.bottom()),
std::round(scale * header_padding.right()));
inter_button_spacing_ = std::round(scale * kHeaderSpacing);
for (auto type : display_types) {
double button_height =
scale * (button_sizes[type].height() + button_margins[type].top() +
button_margins[type].bottom());
double available_height =
top_area_height -
scale * (header_padding.top() + header_padding.bottom());
double scaled_button_offset = (available_height - button_height) / 2;
MySize size = button_sizes[type];
size = MySize(std::round(scale * size.width()),
std::round(scale * size.height()));
MyInset margin = button_margins[type];
margin =
MyInset(std::round(scale * (header_padding.top() + margin.top()) +
scaled_button_offset),
std::round(scale * margin.left()), 0,
std::round(scale * margin.right()));
button_margins_[type] = margin;
for (size_t state = 0; state < VLC_QT_THEME_BUTTON_STATE_COUNT; state++) {
auto buttonState = static_cast<vlc_qt_theme_csd_button_state>(state);
std::unique_ptr<NavButtonImageSource> source = std::make_unique<NavButtonImageSource>(
type, buttonState,
maximized, active, size);
button_images_[type][buttonState] = source->GetImageForScale(scale);
}
}
}
VLCPicturePtr NavButtonProviderGtk::GetImage(
vlc_qt_theme_csd_button_type type,
vlc_qt_theme_csd_button_state state) const {
auto it = button_images_.find(type);
assert(it != button_images_.end());
auto picIt = it->second.find(state);
assert(picIt != it->second.cend());
return picIt->second;
}
MyInset NavButtonProviderGtk::GetNavButtonMargin(
vlc_qt_theme_csd_button_type type) const {
auto it = button_margins_.find(type);
assert(it != button_margins_.end());
return it->second;
}
MyInset NavButtonProviderGtk::GetTopAreaSpacing() const {
return top_area_spacing_;
}
int NavButtonProviderGtk::GetInterNavButtonSpacing() const {
return inter_button_spacing_;
}
} // namespace gtk
/*****************************************************************************
* Copyright (C) 2022 VLC authors and VideoLAN
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
// original code from the Chromium project
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_GTK_NAV_BUTTON_PROVIDER_GTK_H_
#define UI_GTK_NAV_BUTTON_PROVIDER_GTK_H_
#include "gtk_compat.h"
#include "../qtthemeprovider.hpp"
#include <map>
namespace gtk {
class NavButtonProviderGtk {
public:
NavButtonProviderGtk();
~NavButtonProviderGtk();
// views::NavButtonProvider:
void RedrawImages(int top_area_height, bool maximized, bool active);
VLCPicturePtr GetImage(vlc_qt_theme_csd_button_type type,
vlc_qt_theme_csd_button_state state) const;
MyInset GetNavButtonMargin(
vlc_qt_theme_csd_button_type type) const;
MyInset GetTopAreaSpacing() const;
int GetInterNavButtonSpacing() const;
private:
std::map<vlc_qt_theme_csd_button_type,
std::map<vlc_qt_theme_csd_button_state, VLCPicturePtr>>
button_images_;
std::map<vlc_qt_theme_csd_button_type, MyInset>
button_margins_;
MyInset top_area_spacing_;
int inter_button_spacing_;
};
} // namespace gtk
#endif // UI_GTK_NAV_BUTTON_PROVIDER_GTK_H_
/*****************************************************************************
* Copyright (C) 2022 VLC authors and VideoLAN
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
// original code from the Chromium project
// Copyright 2017 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef UI_BASE_GLIB_SCOPED_GOBJECT_H_
#define UI_BASE_GLIB_SCOPED_GOBJECT_H_
#include <glib-object.h>
#include <cassert>
// Similar to a std::shared_ptr for GObject types.
template <typename T>
class ScopedGObject {
public:
ScopedGObject() = default;
ScopedGObject(const ScopedGObject<T>& other) : obj_(other.obj_) { Ref(); }
ScopedGObject(ScopedGObject<T>&& other) : obj_(other.obj_) {
other.obj_ = nullptr;
}
~ScopedGObject() { Unref(); }
ScopedGObject<T>& operator=(const ScopedGObject<T>& other) {
Unref();
obj_ = other.obj_;
Ref();
return *this;
}
ScopedGObject<T>& operator=(ScopedGObject<T>&& other) {
Unref();
obj_ = other.obj_;
other.obj_ = nullptr;
return *this;
}
T* get() { return obj_; }
operator T*() { return obj_; }
private:
template <typename U>
friend ScopedGObject<U> TakeGObject(U* obj);
template <typename U>
friend ScopedGObject<U> WrapGObject(U* obj);
explicit ScopedGObject(T* obj) : obj_(obj) {}
void RefSink() {
// Remove the floating reference from |obj_| if it has one.
if (obj_ && g_object_is_floating(obj_))
g_object_ref_sink(obj_);
}
void Ref() {
if (obj_) {
assert(!g_object_is_floating(obj_));
g_object_ref(obj_);
}
}
// This function is necessary so that gtk can overload it in
// the case of T = GtkStyleContext.
void Unref() {
if (obj_)
g_object_unref(obj_);
}
T* obj_ = nullptr;
};
// Create a ScopedGObject and do not increase the GObject's reference count.
// This is usually used to reference a newly-created GObject, which are created
// with a reference count of 1 by default.
template <typename T>
ScopedGObject<T> TakeGObject(T* obj) {
ScopedGObject<T> scoped(obj);
scoped.RefSink();
return scoped;
}
// Create a ScopedGObject and increase the GObject's reference count by 1.
// This is usually used to reference an existing GObject.
template <typename T>
ScopedGObject<T> WrapGObject(T* obj) {
ScopedGObject<T> scoped(obj);
scoped.Ref();
return scoped;
}
#endif // UI_BASE_GLIB_SCOPED_GOBJECT_H_
/*****************************************************************************
* Copyright (C) 2022 VLC authors and VideoLAN
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#ifndef QTTHEMEPROVIDER_HPP
#define QTTHEMEPROVIDER_HPP
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <vlc_common.h>
#include <vlc_plugin.h>
#define VLC_QT_INTF_PUBLIC_COLORS(X) \
X(text) \
X(textInactive) \
X(textDisabled) \
X(bg) \
X(bgInactive) \
X(bgAlt) \
X(bgAltInactive) \
X(bgHover) \
X(bgHoverText) \
X(bgHoverInactive) \
X(bgHoverTextInactive) \
X(bgFocus) \
X(button) \
X(buttonText) \
X(buttonBorder) \
X(textActiveSource) \
X(topBanner) \
X(lowerBanner) \
X(accent) \
X(alert) \
X(separator) \
X(playerControlBarFg) \
X(expandDelegate) \
X(tooltipTextColor) \
X(tooltipColor) \
X(border) \
X(buttonHover) \
X(buttonBanner) \
X(buttonPrimaryHover) \
X(buttonPlayer) \
X(grid) \
X(gridSelect) \
X(listHover) \
X(textField) \
X(textFieldHover) \
X(icon) \
X(sliderBarMiniplayerBgColor) \
X(windowCSDButtonBg)
#define DEFINE_QCOLOR_STRUCT(x) void* x;
struct vlc_qt_palette_t
{
VLC_QT_INTF_PUBLIC_COLORS(DEFINE_QCOLOR_STRUCT)
};
#undef DEFINE_QCOLOR_STRUCT
enum vlc_qt_theme_image_type
{
VLC_QT_THEME_IMAGE_TYPE_CSD_BUTTON
};
enum vlc_qt_theme_csd_button_type {
VLC_QT_THEME_BUTTON_MAXIMIZE = 0,
VLC_QT_THEME_BUTTON_MINIMIZE,
VLC_QT_THEME_BUTTON_RESTORE,
VLC_QT_THEME_BUTTON_CLOSE,
VLC_QT_THEME_BUTTON_TYPE_COUNT
};
enum vlc_qt_theme_csd_button_state {
VLC_QT_THEME_BUTTON_STATE_DISABLED = 0,
VLC_QT_THEME_BUTTON_STATE_HOVERED,
VLC_QT_THEME_BUTTON_STATE_NORMAL,
VLC_QT_THEME_BUTTON_STATE_PRESSED,
VLC_QT_THEME_BUTTON_STATE_COUNT
};
struct vlc_qt_theme_image_setting {
float windowScaleFactor;
float userScaleFacor;
union {
struct {
vlc_qt_theme_csd_button_type buttonType;
vlc_qt_theme_csd_button_state state;
bool maximized;
bool active;
int bannerHeight;
} csdButton;
} u;
};
struct vlc_qt_theme_metrics {
union {
struct {
int interNavButtonSpacing;
int csdFrameMarginLeft;
int csdFrameMarginRight;
int csdFrameMarginTop;
int csdFrameMarginBottom;
} csd;
} u;
};
struct vlc_qt_theme_provider_t
{
struct vlc_object_t obj;
void* p_sys;
//set by user while opening
void (*paletteUpdated)(vlc_qt_theme_provider_t* obj, void* data);
void* paletteUpdatedData;
void (*metricsUpdated)(vlc_qt_theme_provider_t* obj, vlc_qt_theme_image_type type, void* data);
void* metricsUpdatedData;
void (*setColorInt)(void* color, int r, int g, int b, int a);
void (*setColorF)(void* color, double r, double g, double b, double a);
//set by module
void (*close)(vlc_qt_theme_provider_t* obj);
bool (*isThemeDark)(vlc_qt_theme_provider_t* obj);
void (*updatePalette)(vlc_qt_theme_provider_t* obj, struct vlc_qt_palette_t*);
picture_t* (*getThemeImage)(vlc_qt_theme_provider_t* obj, vlc_qt_theme_image_type type, const vlc_qt_theme_image_setting* setting);
bool (*getThemeMetrics)(vlc_qt_theme_provider_t* obj, vlc_qt_theme_image_type type, vlc_qt_theme_metrics* setting);
bool (*supportThemeImage)(vlc_qt_theme_provider_t* obj, vlc_qt_theme_image_type type);
};
#endif // QTTHEMEPROVIDER_HPP
/*****************************************************************************
* Copyright (C) 2020 VLC authors and VideoLAN
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
*****************************************************************************/
#include "systempalette.hpp"
#include <QGuiApplication>
#include <QPalette>
#include <QSettings>
#include <QFile>
#include <maininterface/mainctx.hpp>
#include <vlc_modules.h>
namespace {
QColor blendColors(QColor c1, QColor c2, float blend = 0.5)
{
return QColor::fromRgbF(c2.redF() + (c1.redF() - c2.redF()) * blend,
c2.greenF() + (c1.greenF() - c2.greenF()) * blend,
c2.blueF() + (c1.blueF() - c2.blueF()) * blend,
c2.alphaF() + (c1.alphaF() - c2.alphaF()) * blend);
}
QColor setColorAlpha(const QColor& c1, float alpha)
{
QColor c(c1);
c.setAlphaF(alpha);
return c;
}
#ifndef _WIN32
/**
* function taken from QtBase gui/platform/unix/qgenericunixservices.cpp
*/
static inline QByteArray detectLinuxDesktopEnvironment()
{
const QByteArray xdgCurrentDesktop = qgetenv("XDG_CURRENT_DESKTOP");
if (!xdgCurrentDesktop.isEmpty())
return xdgCurrentDesktop.toUpper(); // KDE, GNOME, UNITY, LXDE, MATE, XFCE...
// Classic fallbacks
if (!qEnvironmentVariableIsEmpty("KDE_FULL_SESSION"))
return QByteArrayLiteral("KDE");
if (!qEnvironmentVariableIsEmpty("GNOME_DESKTOP_SESSION_ID"))
return QByteArrayLiteral("GNOME");
// Fallback to checking $DESKTOP_SESSION (unreliable)
QByteArray desktopSession = qgetenv("DESKTOP_SESSION");
// This can be a path in /usr/share/xsessions
int slash = desktopSession.lastIndexOf('/');
if (slash != -1) {
QSettings desktopFile(QFile::decodeName(desktopSession + ".desktop"), QSettings::IniFormat);
desktopFile.beginGroup(QStringLiteral("Desktop Entry"));
QByteArray desktopName = desktopFile.value(QStringLiteral("DesktopNames")).toByteArray();
if (!desktopName.isEmpty())
return desktopName;
// try decoding just the basename
desktopSession = desktopSession.mid(slash + 1);
}
if (desktopSession == "gnome")
return QByteArrayLiteral("GNOME");
else if (desktopSession == "xfce")
return QByteArrayLiteral("XFCE");
else if (desktopSession == "kde")
return QByteArrayLiteral("KDE");
return QByteArrayLiteral("UNKNOWN");
}
bool isGTKBasedEnvironment()
{
QList<QByteArray> gtkBasedEnvironments{
"GNOME",
"X-CINNAMON",
"UNITY",
"MATE",
"XFCE",
"LXDE"
};
const QList<QByteArray> desktopNames = detectLinuxDesktopEnvironment().split(':');
for (const QByteArray& desktopName: desktopNames)
{
if (gtkBasedEnvironments.contains(desktopName))
return true;
}
return false;
}
#endif
static void PaletteChangedCallback(vlc_qt_theme_provider_t*, void* data)
{
auto priv = static_cast<ExternalPaletteImpl*>(data);
emit priv->paletteChanged();
}
static void MetricsChangedCallback(vlc_qt_theme_provider_t*, vlc_qt_theme_image_type type, void* data)
{
auto priv = static_cast<ExternalPaletteImpl*>(data);
assert(priv);
priv->updateMetrics(type);
}
static void ReleaseVLCPictureCb(void* data)
{
auto pic = static_cast<picture_t*>(data);
if (pic)
picture_Release(pic);
}
static void setQColorRBGAInt(void* data, int r, int g, int b, int a)
{
auto color = static_cast<QColor*>(data);
color->setRgb(r,g,b,a);
}
static void setQColorRBGAFloat(void* data, double r, double g, double b, double a)
{
auto color = static_cast<QColor*>(data);
color->setRgbF(r,g,b,a);
}
}
ExternalPaletteImpl::ExternalPaletteImpl(MainCtx* ctx, QObject* parent)
: QObject(parent)
, m_ctx(ctx)
{
}
ExternalPaletteImpl::~ExternalPaletteImpl()
{
if (m_provider)
{
if (m_provider->close)
m_provider->close(m_provider);
if (m_module)
module_unneed(m_provider, m_module);
vlc_object_delete(m_provider);
}
}
bool ExternalPaletteImpl::init()
{
QString preferedProvider;
#ifndef _WIN32
if (isGTKBasedEnvironment())
preferedProvider = "qt-themeprovider-gtk";
#endif
m_provider = static_cast<vlc_qt_theme_provider_t*>(vlc_object_create(m_ctx->getIntf(), sizeof(vlc_qt_theme_provider_t)));
if (!m_provider)
return false;
m_provider->paletteUpdated = PaletteChangedCallback;
m_provider->paletteUpdatedData = this;
m_provider->metricsUpdated = MetricsChangedCallback;
m_provider->metricsUpdatedData = this;
m_provider->setColorF = setQColorRBGAFloat;
m_provider->setColorInt = setQColorRBGAInt;
m_module = module_need(m_provider, "qt theme provider",
preferedProvider.isNull() ? nullptr : qtu(preferedProvider),
true);
if (!m_module)
return false;
return true;
}
bool ExternalPaletteImpl::isThemeDark() const
{
if (!m_provider->isThemeDark)
return false;
return m_provider->isThemeDark(m_provider);
}
bool ExternalPaletteImpl::hasCSDImages() const
{
if (!m_provider->supportThemeImage)
return false;
return m_provider->supportThemeImage(m_provider, VLC_QT_THEME_IMAGE_TYPE_CSD_BUTTON);
}
QImage ExternalPaletteImpl::getCSDImage(vlc_qt_theme_csd_button_type type, vlc_qt_theme_csd_button_state state, bool maximized, bool active, int bannerHeight)
{
if (!m_provider->getThemeImage)
return {};
vlc_qt_theme_image_setting imageSettings;
imageSettings.windowScaleFactor = m_ctx->intfMainWindow()->devicePixelRatio();
imageSettings.userScaleFacor = m_ctx->getIntfUserScaleFactor();
imageSettings.u.csdButton.buttonType = type;
imageSettings.u.csdButton.state = state;
imageSettings.u.csdButton.maximized = maximized;
imageSettings.u.csdButton.active = active;
imageSettings.u.csdButton.bannerHeight = bannerHeight;
picture_t* pic = m_provider->getThemeImage(m_provider, VLC_QT_THEME_IMAGE_TYPE_CSD_BUTTON, &imageSettings);
if (!pic)
return {};
QImage::Format format = QImage::Format_Invalid;
switch (pic->format.i_chroma)
{
case VLC_CODEC_ARGB:
format = QImage::Format_ARGB32_Premultiplied;
break;
case VLC_CODEC_RGBA:
format = QImage::Format_RGBA8888_Premultiplied;
case VLC_CODEC_RGB24:
format = QImage::Format_RGB888;
default:
msg_Err(m_ctx->getIntf(), "unexpected image format from theme provider");
break;
}
return QImage(pic->p[0].p_pixels,
pic->format.i_visible_width, pic->format.i_visible_height, pic->p[0].i_pitch,
format,
ReleaseVLCPictureCb, pic
);
}
CSDMetrics* ExternalPaletteImpl::getCSDMetrics() const
{
return m_csdMetrics.get();
}
void ExternalPaletteImpl::update(vlc_qt_palette_t& p)
{
if (m_provider->updatePalette)
m_provider->updatePalette(m_provider, &p);
}
void ExternalPaletteImpl::updateMetrics(vlc_qt_theme_image_type type)
{
if (m_provider->getThemeMetrics)
{
if (type == VLC_QT_THEME_IMAGE_TYPE_CSD_BUTTON)
{
m_csdMetrics.reset();
[&](){
if (!m_provider->getThemeMetrics)
return;
vlc_qt_theme_metrics metrics;
memset(&metrics, 0, sizeof(metrics));
bool ret = m_provider->getThemeMetrics(m_provider, VLC_QT_THEME_IMAGE_TYPE_CSD_BUTTON, &metrics);
if (!ret)
return;
m_csdMetrics = std::make_unique<CSDMetrics>();
m_csdMetrics->interNavButtonSpacing = metrics.u.csd.interNavButtonSpacing;
m_csdMetrics->csdFrameMarginLeft = metrics.u.csd.csdFrameMarginLeft;
m_csdMetrics->csdFrameMarginRight = metrics.u.csd.csdFrameMarginRight;
m_csdMetrics->csdFrameMarginTop = metrics.u.csd.csdFrameMarginTop;
m_csdMetrics->csdFrameMarginBottom = metrics.u.csd.csdFrameMarginBottom;
}();
emit CSDMetricsChanged();
}
}
}
SystemPalette::SystemPalette(QObject* parent)
: QObject(parent)
{
updatePalette();
}
ColorSchemeModel::ColorScheme SystemPalette::source() const
{
return m_source;
}
QImage SystemPalette::getCSDImage(vlc_qt_theme_csd_button_type type, vlc_qt_theme_csd_button_state state, bool maximized, bool active, int bannerHeight)
{
if (!hasCSDImage())
return QImage();
assert(m_palettePriv);
return m_palettePriv->getCSDImage(type, state, maximized, active, bannerHeight);
}
CSDMetrics* SystemPalette::getCSDMetrics() const
{
if (!m_palettePriv)
return nullptr;
return m_palettePriv->getCSDMetrics();
}
bool SystemPalette::hasCSDImage() const
{
if (!m_palettePriv)
return false;
return m_palettePriv->hasCSDImages();
}
QColor SystemPalette::blendColors(const QColor& c1, const QColor& c2, float blend)
{
return ::blendColors(c1, c2, blend);
}
QColor SystemPalette::setColorAlpha(const QColor& c1, float alpha)
{
return ::setColorAlpha(c1, alpha);
}
void SystemPalette::setSource(ColorSchemeModel::ColorScheme source)
{
if (m_source == source)
return;
m_source = source;
updatePalette();
emit sourceChanged();
}
void SystemPalette::setCtx(MainCtx* ctx)
{
if (ctx == m_ctx)
return;
m_ctx = ctx;
emit ctxChanged();
updatePalette();
}
void SystemPalette::updatePalette()
{
m_palettePriv.reset();
switch(m_source)
{
case ColorSchemeModel::System:
makeSystemPalette();
break;
case ColorSchemeModel::Day:
makeLightPalette();
break;
case ColorSchemeModel::Night:
makeDarkPalette();
break;
default:
break;
}
if (m_palettePriv)
{
connect(
m_palettePriv.get(), &ExternalPaletteImpl::paletteChanged,
this, &SystemPalette::updatePalette);
connect(
m_palettePriv.get(), &ExternalPaletteImpl::CSDMetricsChanged,
this, &SystemPalette::CSDMetricsChanged);
}
emit paletteChanged();
bool hasCSDImage = m_palettePriv && m_palettePriv->hasCSDImages();
if (m_hasCSDImage != hasCSDImage)
{
m_hasCSDImage = hasCSDImage;
emit hasCSDImageChanged();
}
}
void SystemPalette::makeLightPalette()
{
//QColor grey1 = QColor{"#9E9E9E"};
QColor grey2 = QColor{"#666666"};
m_isDark = false;
m_text = QColor{"#232627"};
m_textInactive = QColor{"#7f8c8d"};
m_textDisabled = "#bdc3c7";
m_bg = lightGrey100;
m_bgInactive = QColor{"#fcfdfc"};
m_bgAlt = lightGrey400;
m_bgAltInactive = lightGrey400;
m_bgHover = lightGrey400;
m_bgHoverText = m_text;
m_bgHoverInactive = QColor{"#3daee9"};
m_bgHoverTextInactive = m_text;
m_bgFocus = Qt::black;
m_button = QColor{"#eff0f1"};
m_buttonText = m_text;
m_buttonBorder = ::blendColors(m_button, m_buttonText, 0.8);
m_textActiveSource = QColor{"#ff950d"};
m_topBanner = lightGrey400;
m_lowerBanner = Qt::white;
m_accent = orange800;
m_alert = QColor{"#d70022"};
m_separator = lightGrey400;
m_playerControlBarFg = QColor{"#333333"};
m_expandDelegate = Qt::white;
m_tooltipTextColor = Qt::white;
m_tooltipColor = Qt::black;
m_border = QColor{"#e0e0e0"};
m_buttonHover = lightGrey300;
m_buttonBanner = grey2;
m_buttonPrimaryHover = QColor{"#e65609"};
m_buttonPlayer = QColor{"#484848"};
m_grid = lightGrey400;
m_gridSelect = lightGrey600;
m_listHover = lightGrey500;
m_textField = QColor{"#999999"};
m_textFieldHover = QColor{"#4c4c4c"};
m_icon = QColor{"#616161"};
m_sliderBarMiniplayerBgColor = QColor{"#FFEEEEEE"};
m_windowCSDButtonBg = QColor{"#80DADADA"};
}
void SystemPalette::makeDarkPalette()
{
m_isDark = true;
//QColor grey1 = QColor{"#666666"};
//QColor grey2 = QColor{"#AAAAAA"};
m_text = "#eff0f1";
m_textInactive = "#bdc3c7";
m_textDisabled = "#bdc3c7";
m_bg = darkGrey200;
m_bgInactive = "#232629";
m_bgAlt = darkGrey400;
m_bgAltInactive = darkGrey300;
m_bgHover = darkGrey800;
m_bgHoverInactive = "#3daee9";
m_bgHoverText = m_text;
m_bgHoverTextInactive = m_text;
m_bgFocus = Qt::white;
m_button = "#31363b";
m_buttonText = m_text;
m_buttonBorder = "#575b5f";
m_textActiveSource = "#ff950d";
m_topBanner = darkGrey400;
m_lowerBanner = Qt::black;
m_accent = orange500;
m_alert = QColor{"#d70022"};
m_separator = darkGrey700;
m_playerControlBarFg = Qt::white;
m_expandDelegate = Qt::black;
m_tooltipTextColor = Qt::black;
m_tooltipColor = Qt::white;
m_border = darkGrey800;
m_buttonHover = darkGrey800;
m_buttonBanner = QColor("#a6a6a6");
m_buttonPrimaryHover = QColor{"#e67A00"};
m_buttonPlayer = lightGrey600;
m_grid = darkGrey500;
m_gridSelect = darkGrey800;
m_listHover = darkGrey500;
m_textField = QColor{"#6f6f6f"};
m_textFieldHover = QColor{"#b7b7b7"};
m_icon = Qt::white;
m_sliderBarMiniplayerBgColor = QColor{"#FF929292"};
m_windowCSDButtonBg = QColor{"#80484848"};
}
void SystemPalette::makeSystemPalette()
{
if (!m_ctx)
{
//can't initialise system palette, fallback to default
makeLightPalette();
return;
}
auto palette = std::make_unique<ExternalPaletteImpl>(m_ctx);
if (!palette->init())
{
//can't initialise system palette, fallback to default
makeLightPalette();
return;
}
if (palette->isThemeDark())
makeDarkPalette();
else
makeLightPalette();
vlc_qt_palette_t p;
#define BIND_COLOR(name) p. name = &m_##name;
VLC_QT_INTF_PUBLIC_COLORS(BIND_COLOR)
#undef BIND_COLOR
palette->update(p);
m_palettePriv = std::move(palette);
}