[Kde-games-devel] Draft for the core KgTheme API

Parker Coates coates at kde.org
Fri Mar 16 16:29:00 UTC 2012


On Thu, Mar 15, 2012 at 18:48, Stefan Majewsky wrote:
> On Thu, Mar 15, 2012 at 4:22 AM, Parker Coates wrote:
>> See kdegames/kpat/libkcardgame/kcardtheme.h. Obviously it's not an
>> apples to apples comparison, but there's plenty of common requirements.
>
> Interesting read. KgTheme does not have anything like
> KCardTheme::supportedFeatures, but a filtering API can be added to
> KgThemeProvider.

I think the supportedFeatures stuff is going away. I see now that it
was an overengineered solution to a thus-far theoretical use case. If
there proves to be a real need for it in the future, it can be
re-added easily enough.

> And of course KCardTheme could not use the discovery
> algorithms from KgTheme, which operate on files rather than
> directories.
>
>> Don't
>> judge too harshly, though as I have some in improvements in progress
>> in a work branch. :)
>
> apidox? *hint hint*

In progress in said branch. The problem with writing API docs is that
they make you notice problems with your API. :) And fixing those
problems is way more interesting than documenting, so the documenting
never gets finished.

>> The main highlights:
>> * KCardTheme objects have no non-const methods (including setters). I
>> couldn't find a use case in which a theme object ever needed to change
>> after its creation.
>
> KgTheme implements this via simple runtime assertions, in a way that
> is identical to how KgDifficultyLevel constness is ensured.

I think the question still stands, what would the KgTheme setters be
used for? It's not that KCardTheme has to be immutable, I just didn't
see a reason to make it mutable. Mutability opened the door to more
complex scenarios like

kgThemeProvider::currentTheme()->setSvgFile( "/some/new/path.graphics.svgz" );

Is the new SVG file used?

> Granted,
> KgDifficultyLevel does not have non-const methods, but subclasses
> could add some. Subclassing KgDifficultyLevel and KgTheme is
> explicitly supported, although KgTheme needs some extensions to
> actually embrace it.

I'm curious. What's the use case for subclassing either of them?

>> * KCardTheme objects are implicitly shared, so they are always passed
>> by value. Because pointers are never used, one never needs to worry
>> about who "owns" the theme object and needs to delete it. As a bonus,
>> because all methods are const, copies never detach.
>
> Hm, it did not occur to me that KgTheme (and KgDifficultyLevel, for
> that matter) have value semantics. They do, but still I'm more
> comfortable with object semantics: First, I don't have to worry about
> who deletes the instances when there's a KgThemeProvider. Second,
> "const KgTheme&" looks like something I can easily copy and modify if
> I like. "const KgTheme*" states more clearly that there is an object
> which you can look at but don't you dare copy it.

const doesn't say anything about copyability to me, only mutability.

>> * Finding themes is done with a single static method and there's no
>> notion of a provider. KgThemeProvider mainly provides methods for
>> setting and getting the current and default themes, but personally I'd
>> much rather use KConfig(XT) for this.
>
> First, input accepted: I'll make the discovery logic available from
> KgTheme if one does not like to use KgThemeProvider.

I'm not sure that's a good idea. Multiple ways of doing the same thing
isn't really a step forward. If KgThemeProvider is to be *the* class
for finding themes, it should be the class for finding themes. See my
later comment on compromises.

> Re KConfig(XT), why would you rather use it for this? Storing and
> restoring a theme selection is boiler-plate that can be trivially
> automated.

Yes, but it's trivially small bit of boilerplate and let's my
configuration be handled by my configuration class. :)

> Plus, KgThemeProvider gives you automatic propagation of the theme
> selection to KGameRenderer. In KPat, for example, you'd only have to
> connect KgThemeProvider::currentThemeChanged to Dealer::relayoutScene
> manually. Everything else in MainWindow::appearanceChanged is gone.

KPat isn't a particularly good example as it has two parallel theming systems.

> And then, there's QML. As far as I can see, calling from QML into C++
> code, even if only for a single KConfig(XT) method call, usually
> generates a disproportional amount of boilerplate code. Then it's even
> nicer if this stuff just happens by itself.

I can't comment on QML stuff as I've never done QML to C++ bridging,
but I'd be surprised if there isn't (or won't soon be) a way to access
KConfig from QML, since so many KDE components are moving that way and
KConfig is so ubiquitous withing KDE.

> I actually plan to go further on this road and add
> KgAudioScene::enableSounds() and a corresponding KStandardGameAction.
> I like that e.g. Bomber and Kapman could then completely drop their
> KConfigXT files since everything in there has moved into libkdegames.
> I honestly did not expect these changes to be controversial.
>
> Please don't take this as if I try to force this upon you. My
> reasoning behind about every component I added to libkdegames in the
> last years was to move code complexity either into the library (where
> possible) or into the initialization. The second part is important for
> the big picture.
>
> When I started, about all KDE games had a big bunch of code that just
> deals with updates following theme changes or window resize events. A
> part of KGameRenderer's original intention was to get rid of this
> update code by letting the framework do as much as possible of it
> automagically. This pattern is not as important in KgAudio, since
> there are no big updates necessary until we introduce sound themes,
> but the fire-and-forget usage of KgSound::start() is reminiscent of
> the original intent.
>
> KgDifficulty and KgThemeProvider finally fully implement what one
> could call the construct-and-forget pattern. You set up these objects,
> perhaps wire up one change signal, and you'll never have to worry
> about them again. The construct-and-forget pattern is also a guiding
> principle behind QML: A QML file is basically a huge constructor for a
> QML element with multitudes of subelements.

These four paragraphs have really pointed out the disparity here. You
and I have quite different views of what libkdegames should be, in a
big picture sense.

I see libkdegames as a set of tools that game authors can use when
building there games to avoid recreating existing functionality and to
provide greater consistency across games. I guess this is kind of the
classic Unix view of tools. Each should do one thing, do it well, and
be easy to combine with the others. This leads to a "the common cases
should be easy to code and coding the uncommon cases should still be
straightforward" approach.

You see libkdegames as a framework within which authors can create
their games. It goes further in avoiding effort duplication and
improving consistency by handling lots of details automagically so
authors don't need to worry about it. This leads to a "the common case
should require little to no code, but the uncommon case requires
coding around the framework" approach. The KXmlGui framework is a
pretty good example of this approach.

I saw (and still see) the KGameRenderer framework as a useful addition
to libkdegames because: 1) The code involved is complex and
duplicating it in every game lead to more work and lower quality
solutions. 2) The automagic close-coupling between the renderer and
its clients solve a tricky technical problem.

On the other hand, I have no problem with simple connector code like
kpat/MainWindow::appearanceChanged() since it's small, straightforward
and explicit. For me as a developer the small cost of writing the code
doesn't justify letting KgThemeProvider automagically find and modify
my config file behind my back. Same for updating the renderer. What if
I have more than one renderer (like KGoldrunner would require)? What
if I have more than config file? What if I have two different
categories of sounds that I want to turn on and off separately. What
if I don't want my difficulty selector in the status bar, but
somewhere else? (To bring up a recent example of the troubles that can
arise with automagic frameworks.)

Neither of us is right or wrong. The history of libkdegames follows
the reusable tools model, but that's not a valid argument against
future change. It saddens me that connector code is apparently so
difficult to use with QML, but if that's the case it certainly
supports your construct-and-forget approach. I do think we should
avoid the temptation to attempt a compromise as I think that will only
lead to a set of classes implementing two distinct APIs, which I can't
see yielding elegant results.

> In a way, that's my contribution to the QML-readiness of libkdegames.
> Sorry if this was too long of a read (I can't even think of a good
> tl;dr) and thanks that you made it all the way through.

Thank you for clarifying where you're coming from. Honestly, I'm not
sure where we go from here. Ideally, I'd like to hear input from some
of the other developers. What do you think folks?

Parker


More information about the kde-games-devel mailing list