将GTK3小部件颜色设置为计算值的正确方法

时间:2019-07-11 17:44:04

标签: haskell gtk3

在我的用户界面(用Haskell编码)中,我想将小部件的背景色和前景色设置为应用程序计算的值(与主题色相反)。我最初使用widgetOverrideBackgroundColorwidgetOverrideColor,尽管它们已被弃用。但是,这些工具最近已停止工作(很公平,已弃用)。

获得widgetOverrideColor及其亲属功能的最简单方法是什么?是否可以通过编程方式为单个窗口小部件生成样式提供程序并在其中设置颜色(窗口小部件也可以动态生成)?还是拦截draw回调的解决方案?如果是这样,我该如何设置颜色,然后将控制权交还给原件?

2 个答案:

答案 0 :(得分:1)

我现在设法使用CSS和截获的draw信号的组合来做到这一点。该代码是Haskell编写的,但是应该可以翻译为其他语言。

基本技术是在draw回调中添加一些额外的Cairo代码以绘制不同的背景,然后使用CSS设置使小部件本身透明。这段代码使用GTK3的gi-gtk库,绘制的cairo库和颜色的colour库。它已从较大的程序中提取并稍作简化。我希望我没有留下任何悬挂的东西。

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; }

幸运的是,我只需要设置背景色,前景色可以是黑色或白色以与背景形成对比。我不知道如何设置任意的前景色。

答案 1 :(得分:0)

GTK documentation建议使用CSS:

  

gtk_widget_override_background_color从3.16版开始已过时,不应在新编写的代码中使用。

     

此功能在基于CSS的渲染上下文中无用。如果要更改小部件呈现其背景的方式,则应通过特定于应用程序的GtkStyleProvider和CSS样式类使用自定义CSS样式。您还可以通过“绘制”信号覆盖小部件的默认绘制,并使用Cairo绘制特定的颜色,而与CSS样式无关。

作为参考,在my own GTK3 application中,我使用以下代码加载自定义CSS:

{-# LANGUAGE OverloadedStrings #-}

import GI.Gtk

main = do
    -- stuff
    prov <- cssProviderNew
    cssProviderLoadFromPath prov "path/to/custom/css/file.css"
    screenGetDefault >>= \case
        Just screen -> styleContextAddProviderForScreen screen prov 800
        Nothing -> return ()

对于CSS文件本身,您可以按其names来引用窗口小部件。以下CSS未经测试,但可以正常工作:

#widgetname1 {
  color: red;
  background-color: green;
}

我不知道是否有一种方法可以纯粹通过编程方式来完成,而无需任何外部CSS文件(即,指定内联CSS);如果找到一种方法,我将更新此答案。