Slider mit Realtime-Update

FlyDial hat sie, WEGA sowieso - die Rede ist von Slidern, bei denen, während man sie verschiebt, die Bildschirmdarstellung aktualisiert wird. dass derartiges in C relativ simpel ist, soll dieser Artikel zeigen.

Grundsätzlich ist zu bedenken, daß mit der unten vorgestellten Routine DoReal-timeSlide nicht Slider von GEM-Fenstern verwaltet werden können; wollen Sie animierte Slider haben, so müssen sich diese in einem Dialog befinden, der in der Regel von einem form_do überwacht wird.

Im RCS

Wie aber muß ein solcher Slider aufgebaut sein? Bild 1 zeigt einen Dialog aus der Sicht des RCS. Man erkennt zwei verschiedene Objekte, die unbedingt benötigt werden: Hintergrund- und Slider-Objekt. Das Hintergrundobjekt ist das Parent für den Slider, der folglich sein Child ist.

Welchen Objekttyp Sie nun im RCS für die jeweiligen Objekte wählen, ist prinzipiell egal. Sie sollten allerdings darauf achten, daß sowohl Hintergrund- als auch Slider-Objekt die gleiche Breite bzw. Höhe haben, und daß Sie beim Hintergrundobjekt das Füllmuster verändern können. Zum Schluß versehen Sie den Slider noch mit dem TOUCHEXIT-Flag, damit das form_do abbricht, sobald Sie auf diesen klicken.

Der im Bild zu sehende Abstand zwischen Liste und Slidern sollte im Normalfall nicht vorhanden sein; er ist hier nur dazu da, um die Objektanordnung klarer zu machen.

Bevor wir jedoch zur eigentlichen Routine kommen, folgt noch eine kleine Einführung in ein schönes C-Feature, ohne das DoRealtimeSlide nicht möglich wäre...

Funktions-Pointer

Das Problem ist folgendes: Während man den Slider verschiebt, soll irgendetwas am Dialogaufbau geändert werden. Dazu müßte die in Listing 1 beschriebene Routine eine Funktion aufrufen, deren Namen sie aber nicht kennt, also auf dem normale Wege nicht aufrufen kann. Selbstverständlich könnte man in der Routine eigene Funktionen direkt aufrufen; um das Ganze aber universell zu halten, wurde hier der Weg über den Funktions-Pointer gewählt.

Jede Funktion, die Sie in Ihrem Programm deklarieren, steht nach dem Programmstart irgendwo im Speicher. In C erfahren Sie die Stelle, an der die Funktion steht, dadurch, daß Sie den Programmnamen ohne Parameterliste z.B. über ein printf ausgeben. Schreiben Sie also printf( "%p", Routinenname )“, gibt das Programm die Adresse der Funktion aus. Selbstverständlich können Sie den Pointer auf die Funktion auch in einer Variablen speichern, wie es im Funktionskopf von DoRealtimeSlide im Listing 1 zu sehen ist.

Der Parameter call_func zeigt hier auf eine Funktion, die keinen Rückgabewert hat, was man am VOID vor call_func sieht; außerdem wird der Funktion eine Variable vom Typ WORD übergeben, was hinter call_func steht.

Haben Sie also eine Funktion, die diesen Anforderungen entspricht, können Sie deren Anfangsadresse der Routine DoRealtimeSlide übergeben; eine Funktion mit einer anderen Parameterliste eignet sich hierfür nicht. Wie ruft man aber eine Funktion auf, von der man nur die Adresse hat? Die Lösung ist einfach: Der Aufruf erfolgt analog zum normalen Funktionsaufruf. Anstatt des Funktionsnamens schreibt man die Variable, in der man die Adresse der aufzurufenden Funktion gespeichert hat, was in unserem Fall call_func ist, und hängt die Parameterliste an. Ein Aufruf könnte also call_func( 1 ) lauten.

Vorbereitungen

Bevor man die Routine DoRealtimeSlide aufrufen kann, muß man sich die oben erwähnte Funktion schaffen, die dann während des Verschiebens aufgerufen wird. Dieser Funktion wird eine Variable vom Typ WORD übergeben, die die neue Slider-Position angibt; bei vertikalen Slidern ist dies die neue y-Position, bei horizontalen die geänderte x-Position des Sliders. Diese Funktion wird aufgerufen, wenn der Slider seine Position geändert hat. Diesem Wert entsprechend können Sie dann den Dialoginhalt verändern, also z.B. den Inhalt einer Liste nach oben oder unten verschieben.

Der Aufruf

Haben Sie dies alles hinter sich gebracht, müssen Sie in Ihrem form_do nur noch darauf warten, daß der Slider angeklickt wird. Ist dieses Ereignis eingetreten, können Sie die Funktion DoRealtimeSlide aufrufen, der Sie folgende Parameter übergeben:

Der Kern der Routine ist ein evnt_multi, der auf zwei verschiedene Ereignisse wartet, nämlich das Loslassen der linken Maustaste und eine Bewegung der Maus.

Wurde die Maus bewegt, berechnet die Routine die neue Slider-Position und prüft, ob sich überhaupt etwas geändert hat. Ist dies der Fall, wird die via call_func übergebene Funktion aufgerufen, der die neue Slider-Position übergeben wird. Bevor jetzt der Slider an der neuen Position gemalt wird, muß noch ein Teil des Hintergrundobjektes gezeichnet werden, damit keine Reste vom Slider übrig bleiben. Dieses Geschehen wiederholt sich solange, wie die linke Maustaste gedrückt ist.

Das Grundprinzip sollte jetzt klar sein, einige Funktionen zum Setzen der Slider-Position und -große komplettieren das Ganze dann zum universellen Sliderhandler.

Literatur:

[1] Jankowski, Reschke, Rabich, ATARI ST Profibuch

[2] Uwe Hax, Oliver Scholz, CPX - Teil 2, ST-Computer 4/91


#include <aes.h>
#include <portab.h>

/* Konstanten fur slider_type */
#define VERT_SLIDER 1
#define HORT_SLIDER !VERT_SLIDER

VOID DoRealtimeSlide( OBJECT *dialptr,
                      WORD back_index,
                      WORD slider_index, 
                      WORD slider_type,
                      VOID (*call_func) 
                      (WORD new_pos) );

/*****************************************/
/*   Modul : REALTIME C                  */
/* Aufgabe : Slider in Echtzeit          */
/*   Autor : Markus Hövener              */
/*   Datum : 7.9.1991                    */
/*           (c) MAXON Computer GmbH     */
/*****************************************/

#include <aes.h>
#include <portab.h>

#include "realtime.h"

/*****************************************/
/* Slider in Echtzeit bewegen            */
/*****************************************/
VOID DoRealtimeSlide( OBJECT *dialptr,
            WORD back_index,
            WORD slider_index,
            WORD slider_type,
            VOID (*call_func)
            (WORD new_pos) )
{
    WORD    /* Aktuelle Mauskoordinaten     */
            m_x, m_y,
            /* Anfängliche Mauskoordinaten  */ 
            beg_mx, beg_my,
            /* Anfängliche Sliderpositionen */ 
            dial_x, dial_y,
            /* Koordinaten des 'back_index' */ 
            abs_x, abs_y,
            /* Maximale Sliderposition */ 
            max_pos,
            /* Position zum Vergleich */
            prev_x, prev_y,
            events,
            _void;

    /* Maus als Hand */ 
    graf_mouse( FLAT_HAND, 0L );


    /* Mauskoordinaten holen */ 
    graf_mkstate( &m_x, &m_y, &_void, &_void );
    beg_mx = m_x; 
    beg_my = m_y;


    /* Sliderposition ermitteln */ 
    dial_x = dialptr[slider_index].ob_x; 
    dial_y = dialptr[slider_index].ob_y;

    /* Maximale Verschiebung */ 
    if( slider_type == VERT_SLIDER )
        max_pos = dialptr[back_index].ob_height - dialptr[slider_index].ob_height:
    else
        max_pos = dialptr[back_index].ob_width - dialptr[slider_index].ob_width;


    /* Absolute Koordinaten vom Hintergrundobjekt */
    objc_offset( dialptr, back_index, &abs_x, &abs_y );


    do
    {
        /* Sliderposition merken */ 
        prev_x = dialptr[slider_index].ob_x; 
        prev_y = dialptr[slider_index].ob_y;

        /* Event */
        events = evnt_multi ( /* Maus- und Rechteckevents */ 
                              MU_BUTTON|MU_M1,
                              /* Mausklick */
                              1, 1, 0,
                              /* Das Rechteck */ 
                              1, m_x, m_y, 1, 1, 
                              0, 0, 0, 0, 0,
                              /* Message-Buffer */
                              0L,
                              /* Kein Timer */
                              0, 0,
                              /* Endparameter */ 
                              &m_x, &m_y, 
                              &_void,
                              &_void, &_void, 
                              &_void );


        /* Neue Position errechnen */ 
        if( slider_type — VERT_SLIDER )
            dialptr[slider_index].ob_y = dial_y + m_y - beg_my;
        else
            dialptr[slider_index].ob_x = dial_x + m_x - beg_mx;

        /* Eingrenzen */
        if( slider_type == VERT_SLIDER )
        {
            dialptr[slider_index].ob_y = (dialptr[slider_index].ob_y < 0) ? 0
            : dialptr[slider_index].ob_y; 
            dialptr[slider_index].ob_y = (dialptr[slider_index].ob_y > max_pos) ? max_pos
            : dialptr[slider_index].ob_y;
        }
        else
        {
            dialptr[slider_index].ob_x = (dialptr[slider_index] ob_x < 0) ? 0
            : dialptr[slider_index].ob_x; 
            dialptr[slider_index].ob_x = (dialptr[slider_index].ob_x > max_pos)
            ? max:pos dialptr[slider_index].ob_x;
        }

        /*****************************************/
        /* Änderung                              */
        /*****************************************/
        if( (dialptr[slider_index].ob_y != prev_y) || (dialptr[slider_index].ob_x != prev_x) )
        {
            /* Funktion evtl. aufrufen */ 
            if( call_func ) 
                call_func(dialptr[slider_index].ob_y );


            /* Den Hintergrund neumalen */ 
            if( slider_type == VERT_SLIDER )
            {
                if( dialptr[slider_index].ob_y > prev_y ) 
                    objc_draw( dialptr, back_index, 1, abs_x,
                    abs_y + prev_y - 1, dialptr[back_index].ob_width, 
                    dialptr[slider_index].ob_y - prev_y );
                else
                    objc_draw( dialptr, back_index,
                               1, abs_x, abs_y + dialptr[slider_index].ob_y +
                               dialptr[slider_index].ob_height,
                               dialptr[back_index].ob_width,
                               prev_y - dialptr[slider_index].ob_y + 1 );
            }
            else
            {
                if( dialptr[slider_index].ob_x > prev_x ) 
                    objc_draw( dialptr, back_index,
                               1, abs_x + prev_x - 1, abs_y,
                               dialptr[slider_index].ob_x - prev_x, 
                               dialptr[back_index].ob_height );
                else
                    objc_draw( dialptr, back_index,
                               1, abs_x + dialptr[slider_index].ob_x +
                               dialptr[slider_index].ob_width,
                               abs_y, prev_x -
                               dialptr[slider_index].ob_x + 1,
                               dialptr[back_index].ob_height );
            }

            /* ... und den Slider neumalen */ 
            objc_draw( dialptr, slider_index, 1, 
                       abs_x, abs_y +
                       dialptr[slider_index].ob_y - 1, 
                       abs_x + dialptr[slider_index].ob_width,
                       abs_y + dialptr[slider_index].ob_y + 
                       dialptr[slider_index].ob_height );
        }
    }
    while( !(events & MU_BUTTON) );

    /* Normale Maus */ 
    graf_mouse( ARROW, 0L );

Markus Hövener
Aus: ST-Computer 12 / 1991, Seite 88

Links

Copyright-Bestimmungen: siehe Über diese Seite