0073: The Frame, Part II – Shadows & CSS

Frame Off

Results of this example:
Current example output
Current example output
Current example terminal output
Current example terminal output (click for enlarged view)

Today, we start with the irony of looking at the invisible Frame. Turning off visibility is one way of dealing with the aesthetic problem we talked about last time and this is done with a call to setShadowType() as seen in the FrameOff class:

class FrameOff : Frame
{
	private:
	string titleLabel = "Coordinates";
	FrameChildGrid frameChildGrid;
	
	public:
	this()
	{
		super(titleLabel);
		setShadowType(ShadowType.NONE);
		frameChildGrid = new FrameChildGrid();
		add(frameChildGrid);
		
	} // this()
	
} // class FrameOff

This class is identical to the FrameOn class we looked at last time, with the exception of this one statement. ShadowType.NONE gives the results seen in the above screen shot.

Frame Styling

Turning visibility on or off is considered styling, and so is giving the Frame a 3D look. But whereas we use setShadowType() to set visibility, all other styling is done with CSS.

Styling Frames with CSS

Results of this example:
Current example output
Current example output
Current example terminal output
Current example terminal output (click for enlarged view)

To use CSS in GTK, we need to do three things:

  • write a CSS file (which can be an actual file or handled as a string within your code),
  • associate it with our application’s GDK Screen, and
  • tell our application to use this association.

But before we dive in, let’s start with a point of basic terminology…

Step One: Writing a CSS File

In CSS (Cascading Style Sheet) terms, the bits of code used to set properties for various elements are called selectors. For GTK’s purposes, selectors can be written in files ending in the .css extension similar to this:

border
{
	border-style: outset;
/*	border-style: inset; */
/*	border-style: solid; */
/*	border-style: none; */
}

In this example, I’ve laid out the four possible styles of the Frame border while commenting out three of them. Here’s what these options mean:

  • outset: give the Frame a 3D styling and make it look raised from the ‘page,’
  • inset: another 3D styling, this time seemingly recessed into the ‘page,’
  • solid: a plain, solid line, and
  • none: which has the same effect as setShadowType(ShadowType.NONE).

If we save this file in a local css directory with the name frame.css, step one is done. Now let’s look at…

Step Two: The Association Between a CSS File and Our Application

This is a two-step process, with a total of five sub-steps, in which we:

  • have the widget provide its StyleContext to the CSS object,
  • instantiate a CssProvider object which serves as a wrapper of sorts for our CSS file (css/frame.css),
  • pull in that .css file, and
  • set up the association between the CSS file and the StyleContext.

That code, encapsulated in a nice, neat package looks like this:

class CSS // GTK4 compliant
{
	CssProvider provider;
	string cssPath = "./css/frame.css";

	this(StyleContext styleContext)
	{
		provider = new CssProvider();
		provider.loadFromPath(cssPath);
		styleContext.addProvider(provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
		
	} // this()	
	
} // class CSS

Step Three: Telling Our Widget to Use CSS

This is the simplest step because it’s one statement. However, it has to be done for every Widget that needs CSS decoration.

First, in the class attributes, we declare the CSS object:

CSS css = new CSS();

Then in the class constructor, we add this line:

css = new CSS(getStyleContext());

Which means our entire FrameOn class will look like this:

class FrameOn : Frame
{
	private:
	CSS css;
	string titleLabel = "Coordinates";
	FrameChildGrid frameChildGrid;
	float xAlign = 0.05, yAlign = 0.5;

	public:
	this()
	{
		super(titleLabel);
		css = new CSS(getStyleContext());

		setLabelAlign(xAlign, yAlign);
		frameChildGrid = new FrameChildGrid();
		add(frameChildGrid);
		
	} // this()
	
} // class FrameOn

CSS Property Names

Disclaimer: The following demo is downright ugly and is meant solely to demonstrate coding technique, not design sensibilities.

Results of this example:
Current example output
Current example output
Current example terminal output
Current example terminal output (click for enlarged view)

The reason why CSS names work is because GTK has a convention for naming widgets and their properties. Some names are set up by GTK, but you can also roll up your sleeves and add some yourself.

For the sake of demonstration, let’s assume you wanna gain control over a Label’s background color. In class form, it might look like this:

class CSSLabel : Label
{
	CSS css;
		
	this(string textLabel, string cssName)
	{
		super(textLabel);
		
		// css stuff
		setName(cssName);
		css = new CSS(getStyleContext());

	} // this()
	
} // class CSSLabel

Now our .css file can refer to our Label like this:

label#label1
{
	font-family: Arial, Helvetica, sans-serif;
	color: red;
	font-size: 26px;
	background-color: yellow;
	
} /* label1 */

The selector name, label#label1, tells GTK that only widgets derived from an entity with a CSS name of label—and further, these descendants must be named label1—only those widgets will get this styling. Leaving out this drill-down aspect, we could also write the selector name like this:

#label1

Drop the CSS class into your application and you’re off to the races.

CSS and the GTK Inspector

With your application up and running, you can use the GTK Inspector by hitting Ctrl-Shift-D (how apropos) to look at CSS names, both the ones you yourself have assigned and the ones assigned by default. Not all CSS names show up in the Inspector, but here’s a handy reference.

And if I haven’t covered enough detail in this writing, you can find more on CSS and GTK here.

Conclusion

And that’s the basics of both Frames and CSS. It should be remembered, however, that it’s easy to get carried away and over-style an application. Sometime down the road, we may come back to this subject, but it’s an issue of design rather than coding, so it’s a low priority for now.

Until next time.

Comments? Questions? Observations?

Did we miss a tidbit of information that would make this post even more informative? Let's talk about it in the comments.

You can also subscribe via RSS so you won't miss anything. Thank you very much for dropping by.

© Copyright 2023 Ron Tarrant