Теперь мне удалось сделать это с помощью комбинации CSS и перехваченных draw
сигналов. Код написан на Haskell, как и я, но он должен быть переведен на другие языки.
Основной метод заключается в добавлении некоторого дополнительного кода Cairo к обратному вызову draw
для рисования другого фона, а затем с помощью CSS установите прозрачность самого виджета. Этот код использует библиотеку gi-gtk для GTK3, библиотеку cairo для рисования и библиотеку color для цветов. Это было извлечено и немного упрощено из более крупной программы. Надеюсь, я не оставил ничего висящего.
import qualified GI.Cairo.Structs.Context as Gtk
import qualified GI.Gtk as Gtk
import qualified Graphics.Rendering.Cairo as Cairo
import qualified Graphics.Rendering.Cairo.Internal as CI
import qualified Graphics.Rendering.Cairo.Types as Cairo (Cairo (Cairo))
import qualified Data.Colour as C
import qualified Data.Colour.CIE as C
import qualified Data.Colour.SRGB as C
customPaint :: (Gtk.isWidget w) => w -> Maybe Colour -> Gtk.Context -> IO ()
customPaint widget Nothing _ = do
-- No background, so reset everything.
style <- Gtk.widgetGetStyleContext widget
mapM_ (Gtk.styleContextRemoveClass style) [lightClass, darkClass]
customPaint widget (Just c) ctx = do
-- Get the dimensions of the background.
w <- Gtk.widgetGetAllocatedWidth widget
h <- Gtk.widgetGetAllocatedHeight widget
-- Set the widget style to transparent using a class.
style <- Gtk.widgetGetStyleContext widget
mapM_ (Gtk.styleContextRemoveClass style) [lightClass, darkClass]
Gtk.styleContextAddClass style $ if C.luminance c > 0.5 then lightClass else darkClass
-- Draw the background using the Cairo Render monad.
runRender ctx $ do
let
C.RGB r1 g1 b1 = C.toSRGB c
Cairo.setSourceRGB r1 g1 b1
Cairo.rectangle 0 0 (fromIntegral w) (fromIntegral h)
Cairo.fill
-- Conversion between gi-gtk Cairo Context and Cario library Render monad. Only
-- needed because they have different ways of wrapping the underlying C object.
runRender ctx action =
Gtk.withManagedPtr ctx $ \p ->
runReaderT (CI.runRender action) (Cairo.Cairo (castPtr p))
-- CSS class names. "light" uses black text on a pale background. "dark" is the opposite.
lightClass = "transparent-light"
darkClass = "transparent-dark"
Затем вы можете сохранить нужный цвет в IORef и создать обратный вызов для сигнала рисования виджета, например:
Gtk.onWidgetDraw myWidget $ \ctx -> do
c <- readIORef colourRef
customPaint myWidget c ctx
CSS для приложения содержит следующее:
/* Custom CSS classes for coloured widgets.
The background is transparent. The foreground is either black or white.
*/
.hades-transparent-dark {
color: white;
background-color: transparent; }
.hades-transparent-light {
color: black;
background-color: transparent; }
К счастью, мне нужно только установить цвет фона, при этом цвет переднего плана будет черный или белый для контраста с фоном. Я не знаю, как бы я настроил произвольный цвет переднего плана.