Skins and UI

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
7 messages Options
Reply | Threaded
Open this post in threaded view
|

Skins and UI

Günther Schmidt
Hi,

one of my biggest problems with Smalltalk remains to be UI design.

I often see stuff like WinAmps Skin, which seems to be very non-standard
UI, and I have no clue whatsoever how to do this in Dolphin.

Could anybody hint me how to create a Skin/UI like the ones on this website:

        http://www.3dftp.com/skins/skins.htm

How would one do that with Dolphin?

Günther


Reply | Threaded
Open this post in threaded view
|

Re: Skins and UI

Chris Uppal-3
Günther,

> I often see stuff like WinAmps Skin, which seems to be very non-standard
> UI, and I have no clue whatsoever how to do this in Dolphin.
>
> Could anybody hint me how to create a Skin/UI like the ones on this
> website:
>
> http://www.3dftp.com/skins/skins.htm

There are two aspects to this.  One is how to create UIs with odd shapes and
appearance, and the other is how to create skinnable UIs.  There is no real
connection between the two -- leaning how to do one won't help with doing the
other.

For creating weird-looking UIs, there are three main tools/techniques:
clipping regions, transparency, and custom widgets.

Clipping regions are used to tell Windows that a window isn't rectangular.  You
create an irregularly shaped region, and then tell Windows to clip your window
to those bounds.   Clipping regions are exposed in Dolphin as instances of
Region.  Unfortunately Dolphin doesn't include methods, by default, to create
non-rectangular regions (such as ellipses or arbitrary shapes defined by a
path), nor does it expose the interfaces needed to set the clipping region of a
Shell view (afaik, this stuff is only applicable to top-level shells).   I'll
add a bit of code that exposes some of the simpler stuff at the end of this.
See MSDN for the rest of the available API.  With appended code loaded (and I
take /no/ responsibility for the results ;-) you can try something like:

    chb := ClassBrowserShell show.
    r1 := Region ellipse: (0@0 corner: 500@300).
    r2 := Region ellipse: (0@200 corner: 300@500).
    r := r1 union: r2.
    chb view setRegion: r redraw: true.

to get a Very Odd CHB.

Transparency is used to get similar effects, or perhaps even more complicated
ones.  I have no idea which of the two techniques is recommended -- from
limited experimenting I've generally found that clipping regions work better if
they do what you want at all.  Udo Schneider's 'US LayeredView' package from:

    http://udos.swiki.net/3

(when I last looked) exposed the interfaces necessary to use transparency.  You
tell a ShellView (again it has to be a top-level shell) that it is "layered"
using the #isLayered: method from that package, and then you can choose a color
that will be made transparent.  E.g.

    chb := ClassBrowserShell show.
    (chb view)
         isLayered: true;
         colorKey: Color white.

to get another Very Odd CHB.

Once you've got a odd-shaped window, you either won't be able to see the menu
bar and title bar, or you may not have asked Windows to create them at all.  In
that case you have to arrange for how the user can move your window around.
One way is to set a handler for the #onLeftButtonPressed: event that starts a
mouse-drag of the window.  To do that you (in the event handler) create a
MouseTracker, something like:

     (MouseTracker forPresenter: self startingAt: 0@0)
          origin: aMouseEvent position;
          startTracking: self.

which will then call the #{start/continue/cancel/end}TrackingAt: methods of
your presenter.  #startTrackingAt: can just answer its argument.
#endTrackingAt: and #cancelTrackingAt: can just be ignored.  The other one
(which actually moves the window to follow the mouse) can look something like:

    continueTrackingAt: newPoint from: oldPoint
        "private -- called by the MouseTracker as we allow it to drag our
window around.
        Move the window by the indicated amount"

        | oldPos newPos |

        newPoint = oldPoint ifTrue: [^ newPoint].

        oldPos := self view position.
        newPos := oldPos + newPoint - oldPoint.

        self view position: newPos.

         ^ oldPoint. "because we've shifted the reference point"

Of course, you'll have to ensure that the user has ways to close the window,
etc, too.

With that set up, you next need to make your window look "nice" (or slimy,
dripping, and disgusting -- or whatever).  To do that, the easiest thing is to
design an image in a paint program, and use that as your main window.  You can
paint over that (using normal Windows graphics calls) to implement any flashing
lights, or whatever, that your imagination has invented (and your natural good
taste hasn't vetoed ;-).

Lastly, you'll want some buttons (probably very strangely shaped), and perhaps
other widgets, for the user to interact with.  You can implement those
directly, by interpreting mouse movements, etc, yourself, and drawing/updating
your own graphics.  Alternatively, and probably easier in the long run, you can
use custom widgets, such as "owner draw" buttons and so on.  Dolphin doesn't
come with such things in the box, but it's not too difficult to add them
(depending on how ambitious you want to be).  There are several goodies that
contain owner-draw widgets of one sort or another, which may be useful directly
or may help you to work out how to create your own (as I'd guess you will want
to in the long run).  One example is the 'WalicXe - Widgets' package from:

    http://www.walicxe.com/pages/descargas.htm

You'll probably want to place the buttons, etc, at fixed positions (so they are
in the "right" place in the picture you're using as background).  To do that
use a nil #layoutManager.  Or you may want to create a custom layout manager
that -- say -- moves the buttons around at random...

That should be enough to get started with creating some truly vile user
interfaces.  Good luck!

;-)


Skinning is a whole different question.  I can't cover that in any sort of
detail because there are /far/ too may options.  The essence of it is that an
application is skinnable if, and only if, the application's author (or the
creator of the underlying UI toolkit) has built the ability to be skinnable in
from the start.  It isn't something that you can just add afterwards.

Being skinnable requires two things.  One is that the GUI architecture has a
clean separation between how the widgets /look/, and what they /mean/.  Without
that there is no hope of having any kind of pluggable widgetry.  On that front,
you are in luck -- Dolphin has precisely that separation.  In fact Dolphin's
Views are a very flexible and powerful variant on the "skin" idea.  As an
example, I think that the screenshots at:
    http://www.3dftp.com/skins/skins.htm
look pretty lame.  They have outrageously flamboyant decoration, but when you
look at them, the actual lists of files, and so on, are just plain
old-fashioned text.  Nothing fancy at all.  You should be able to say that the
file lists are arranged in a circle, or that the text should seem to creep over
the apparently 3-d surface as your scroll through lists.  None of that is
possible unless you can "plug in" your own special implementation of, say, a
list without affecting the operation of the bits of the application that "know
about" that list.  In Dolphin, that means providing your own View that may look
very different from the standard ListView, but is close enough in behaviour
that the ListPresenter doesn't care which is actually in use.  Of course, the
fancier the plug-in widget has to look, the less chance there is that Windows,
Dolphin, or the community will have implemented it.  So you'll have to write
your own (unless you are satisfied with the "lame" look).

The other half of being skinnable requires that there is some way of specifying
what an application looks like (and maybe some aspects of its behaviour too)
that is /external/ to the application.   According to taste (and according to
how much effort you think it's worth) there is a large range of options here.

At one extreme the "external specification" might be no more than a list of
.GIF files, colours, fonts, button positions, and so on -- the application
would read that file and replace any/all of its own bitmaps, colours, fonts,
etc, with those specified.  I haven't looked at 3dftp really, but I'd guess
that that's about what it does.   A "skin" is then that file plus any
associated GIFs, etc, probably all packaged up together in a ZIP file or
something like that.  You could implement that as easily in Dolphin as in any
other system, but it would be /you/ who did it.  Notice that this isn't really
making much use of the "deep" plugability of Dolphin Views -- all it is doing
is allowing the user to change (some of) the configuration of the existing
application.

At the other extreme, the skin specification is so complex and powerful that
it's really a whole new programming language, and it allows skin-writers to
specify any aspect of the appearance of the application, and maybe even to
re-write it's entire behaviour.  (As I understand it, this is the approach
underlying the Mozzilla toolkit -- which will undoubtedly create security
problems for years to come).  In Dolphin terms, this amounts to replacing the
'V' and most of the 'P' of MVP with the skinning stuff.

What level of skinnability you want is up to you.  The sky's the limit, but is
also a lot of work to reach...

One approach that /might/ work would be intermediate between the above.  If the
"skin" package (zipfile or whatever) was allowed to contain replacement View
instances -- stored in STB format -- then your application could use those in
preference to the configured ones.  (You might need to modify the current View
creation code a bit to make this work).   You could go still further and allow
the skin file to contain new View /classes/ (as binary package files) too.
That would be some extra work, but I think it's probably feasible /if/ you want
a powerful skinning ability in the first place, /and/ are willing to restrict
it to people who can create them in Dolphin.

    -- chris


========  Some Region-related additional methods ===========
!GDILibrary methodsFor!

createEllipticRgnIndirect: aRECT
 "The CreateRectRgnIndirect function creates an elliptcal region.

 HRGN CreateEllipticRgnIndirect(
  CONST RECT *lprc  // pointer to the rectangle
 );"

 <stdcall: handle CreateEllipticRgnIndirect RECT* >
#CUadded.
 ^self invalidCall! !
!GDILibrary categoriesFor: #createEllipticRgnIndirect:!public!win32
functions-region! !

!Region class methodsFor!

ellipse: aRectangle
 "Answer an elliptical region defined by aRectangle"
#CUadded.
 ^self fromOwnedHandle:
  (GDILibrary default createEllipticRgnIndirect: aRectangle asParameter)! !
!Region class categoriesFor: #ellipse:!instance creation!public! !

!View methodsFor!

setRegion: aRegion redraw: aBool
 "Attaches a copy of the given region to this window."

 | hRgn |
#CUadded.
 aRegion isNil ifTrue:
  [^ UserLibrary default
   setWindowRgn: self asParameter
   hRgn: hRgn
   bRedraw: aBool].

 "we have to pass a copy of the region 'cos Windows takes ownership of the
handle"
 hRgn := GDILibrary default createRectRgnIndirect: RECT new.
 GDILibrary default
  combineRgn: hRgn
  hrgnSrc1: aRegion asParameter
  hrgnSrc2: nil
  fnCombineMode: RGN_COPY.

 [^ UserLibrary default
  setWindowRgn: self asParameter
  hRgn: hRgn
  bRedraw: aBool] ifCurtailed: [GDILibrary default deleteObject: hRgn].! !
!View categoriesFor: #setRegion:redraw:!clipping regions!public! !

!UserLibrary methodsFor!

setWindowRgn: hdc hRgn: hRgn bRedraw: aBool
 "The SetWindowRgn function attaches a region to a window.
 NB: After ths has been called, Window's has taken ownership of the region
handle
 and will delete it as necessary.

 int SetWindowRgn(
  HWND hWnd, // handle to window
  HRGN hRgn,  // handle to region
  BOOL bRedraw // window redraw option
 );"

 <stdcall: sword SetWindowRgn handle handle bool>
#CUadded.
 ^self invalidCall! !
!UserLibrary categoriesFor: #setWindowRgn:hRgn:bRedraw:!public!win32
functions-clipping!win32 functions-region! !


Reply | Threaded
Open this post in threaded view
|

Re: Skins and UI

Günther Schmidt
Chris,

thanks once again for your help.

I already found out that the UI stuff is a huge beast to deal with. So
far I haven't even been able to fully understand how "regular" views are
implemented in Dolphin.

The other day I got myself a Petzold so it'd teach me things from the
start. Also quite difficult still as I don't speak C, yet.

I'll try to learn it by constructing my own View using the External
Interfacing documentation from OA.

I'm doing ok with Smalltalk sofar, no bigger problems there, my main
concern still remains about UIs.

Once I'm beyound the above steps, I'll have another look at this book

http://www.amazon.com/exec/obidos/tg/detail/-/1556222491/qid=1122905495/sr=8-1/ref=pd_bbs_1/104-2305678-7675140?v=glance&s=books&n=507846

and hope this gives me some more ideas.

Günther


Ted
Reply | Threaded
Open this post in threaded view
|

Re: Skins and UI

Ted
In reply to this post by Chris Uppal-3
Chris Uppal wrote:
> Günther,
>
>
[ .. snip lots of useful stuff .. ]

Hi Chris,

What an excellent reply. Answers many things I've always wanted to ask
but never got round to.

Cheers!

Ted


Reply | Threaded
Open this post in threaded view
|

Re: Skins and UI

Chris Uppal-3
In reply to this post by Günther Schmidt
Günther,

> I already found out that the UI stuff is a huge beast to deal with.

Probably because you are aiming for ambitious targets (/not/ a criticism!).


> So
> far I haven't even been able to fully understand how "regular" views are
> implemented in Dolphin.

It definitely helps a lot to understand how Window's GUI programming works.
The design (more the internal design than the external API) of View and it's
more Windows-centric subclasses very much reflects how Windows works.

BTW, are you reasonably happy using vanilla Dolphin MVP ?  I mean could you
knock up a fairly simple UI using "normal" tabs, buttons, lists, etc,
reasonably easily ?  If not then you may be trying to run before you can walk
(as they say).


> The other day I got myself a Petzold so it'd teach me things from the
> start. Also quite difficult still as I don't speak C, yet.

Not speaking C will definitely make it hard work.  Fortunately, I don't think
you need to speak C very well in order to get value from Petzold.  I have a
copy of his book too, and find it very helpful, but I've never yet tried to
read any of his complete code examples.  The discussions leading up to the
complete code are good enough on their own (IMO).   What you really need (I
think) is to be able to see, and work with, the correspondence between C
function and structure definitions, and the corresponding code in Dolphin.

    -- chris


Reply | Threaded
Open this post in threaded view
|

Re: Skins and UI

Günther Schmidt
Chris,



> Not speaking C will definitely make it hard work.  Fortunately, I don't think
> you need to speak C very well in order to get value from Petzold.  I have a
> copy of his book too, and find it very helpful, but I've never yet tried to
> read any of his complete code examples.  The discussions leading up to the
> complete code are good enough on their own (IMO).   What you really need (I
> think) is to be able to see, and work with, the correspondence between C
> function and structure definitions, and the corresponding code in Dolphin.
>
>     -- chris
>
>
Yep indeed, that's what I'm working on. Just yesterday I got myself some
books on C.

Boy am I glad that I merely have to understand it to some extent and not
to actually program in C. It must be hell.

There is somebody, I think on Dolphinharbor.org, who seems to be playing
with Windows in Dolphin quite a lot, translucent Windows and such.

I'll try to find that again too, but I think my best bet is to study
Petzold, and try to implement that in Dolphin from Scratch.

Günther


Reply | Threaded
Open this post in threaded view
|

Re: Skins and UI

Günther Schmidt
In reply to this post by Chris Uppal-3
Chris,


> It definitely helps a lot to understand how Window's GUI programming works.
> The design (more the internal design than the external API) of View and it's
> more Windows-centric subclasses very much reflects how Windows works.
>
> BTW, are you reasonably happy using vanilla Dolphin MVP ?  I mean could you
> knock up a fairly simple UI using "normal" tabs, buttons, lists, etc,
> reasonably easily ?  If not then you may be trying to run before you can walk
> (as they say).

Quite so, well I just don't know about the happy business. I mean
creating an App with standard widgets in Dolphin poses no problem, but
going beyond that does.

I understand how to *use* MVP, but so far haven't been able to
understand the inner workings of Dolphins Views yet. As I said it's
quite a beast.


Günther