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

Stefan Majewsky stefan.majewsky at googlemail.com
Tue Mar 20 23:07:37 UTC 2012


On Fri, Mar 16, 2012 at 5:29 PM, Parker Coates <coates at kde.org> wrote:
> 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.

My usual workflow is to write the header including all apidox, and
start the implementation only when everything is finished. To make
that work, I have to think hard about how to implement all the methods
which I'm documenting. When I start writing the implementation, I'm
mostly set.

> 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.

In reference to the classic article
http://doc.qt.nokia.com/qq/qq13-apis.html it is useful to avoid too
much arguments to the constructor and instead offer setters for the
properties. Plus that's also the pattern that QML needs: Suppose you
want to write QML code such as

KgTheme {
    name: "My theme"
    description: "Bla bla bla"
    svgFile: config.getSvgFile()
}

That needs name/description/svgFile as properties. Granted, I cannot
yet come up with a use case for constructing a KgTheme in QML, but it
might arise when e.g. the main QML view instantiates a KGameRenderer
which might instantiate a single KgTheme for it's SVG file. It's nice
to be prepared for these things when it's that easy.

> Mutability opened the door to more
> complex scenarios like
>
> kgThemeProvider::currentTheme()->setSvgFile( "/some/new/path.graphics.svgz" );
>
> Is the new SVG file used?

That does not compile. currentTheme() is a "const KgTheme*" for this
exact reason. Everyone knows that "const_cast" usually implies "here
be dragons", that is: undefined behavior.

>> 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?

With KgDifficultyLevel, the specific use case is to enable additional
properties in custom difficulty levels. KgDifficulty's StandardLevel
enum has only a single value for custom levels, all other distinctions
shall be made by the KgDifficultyLevel instances.

For KgTheme, subclassing can be useful to enable themes with a more
complex structure. For example, KCardTheme could inherit KgTheme and
add an enum for the known cards, a supportedFeatures method (if
desired) etc.

> 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.

I don't feel like this. First, I'm also a proponent of the "Unix
philosophy". Second, libkdegames is not at all a framework to me. It's
a toolkit that contains among other things frameworks (esp. KGame),
but the single parts of libkdegames are cleanly separated. For
example, you do not need to use KgDifficulty if you use KScoreDialog.
KScoreDialog has integration with KgDifficulty to make things easier,
but you can also integrate with your own difficulty classes if you
like.

> 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.

That's a feature to me.

> 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.

KXmlGui is a funny example. I have myself invested quite some time to
hack around it in kolf-ng, and ended up just using it like it was
intended, by modeling different components of my UX into
KXmlGuiClients. The actual problem with KXmlGui IMO is bad
documentation. Those parts which are documented work very well for 99%
of apps.

That does not mean that KXmlGui is without errors. It could definitely
use cleanup, but a documentation update from someone who actually
knows his way around the original design would be even more important.

> 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)?

The KGameRenderer has an explicit constructor-time association to the
KgThemeProvider (or a single static KgTheme), and KgThemeProvider has
a config key to disambiguate the storage location for multiple
providers.

> What if I have two different
> categories of sounds that I want to turn on and off separately.

The nice thing about a KgAudioScene::setEnabled() method is that it
defaults to true, so it won't bother you if you don't use it. Just
like KgDifficulty::setGameRunning(), by the way. As Aaron put it some
time ago, "default settings are easy to manage".

> 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.)

KgDifficulty does not have the difficulty selector by default. You
explicitly have to call KgDifficultyGUI::init. And that's only a
transitional API. Classes like KgDifficultyComboBox etc. will appear
somewhen in the next release cycles. I want different widgets for
touch-optimized interfaces.

> What if I have more than config file?

I moved this quote towards the bottom as a climax because it is very
central for this particular discussion. Once your application has set
some $APPNAME in KAboutData and thus in KComponentData, there is a
component-wide (= application-wide) config at
$KDEHOME/share/config/${APPNAME}rc. This file is referenced by so many
libraries all over the KDE frameworks already.

For example, my kdiamondrc has entries from KXmlGui and KNewStuff. The
size and state of the main window and the "Get more themes" dialog are
automagically remembered across application runs. KHighscore also
unconditionally writes to KGlobal::config(); this will change in
KgScore BTW because it really is too inflexible.

> 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.

As I said, I'm also all for the reusable tools model. Your input on
where I don't meet this standard is very valuable.

> 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.

Just to make this clear: This was just my impression. I'd be glad if
someone proved me wrong, and again I'm counting on GSoC here.

> 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.

I strive for a pattern of one central API with a clear ontology, plus
compatibility methods where required. Which methods serve
compatibility only shall be clearly stated (e.g.
KGameRenderer::frameBaseIndex et.al.).

Greetings
Stefan

P.S. Sorry for the long response time. The weather was finally nice
over the last days, so I had to spend my time outside. :-)


More information about the kde-games-devel mailing list