# Grafiske Brukergrensesnitt (GUI)

## Konstruksjon av grafiske grensesnitt

Grafiske grensesnitt består av en rekke GUI elementer som kalles `widgets`.

## Callback

Et «callback» er en funksjon som kalles etter at en spesifikk hendelse har inntruffet. Disse brukes ofte med brukergrensesnitt, for eksempel for å spesifisere hva som skal skje etter brukeren har trykket på en knapp. Hvert GUI-element lar deg definere én slik funksjon som blir kjørt etter hver interaksjon. Nøyaktig hvilken interaksjon er avhengig av GUI-elementet.

For å vise hvordan man definerer og bruker en callback funksjon, fortsetter vi med samme eksempelet som før:

    #include "AnimationWindow.h"
    #include "widgets/Button.h"

    int main() {
        const TDT4102::Point buttonPosition {100, 100};
        const unsigned int buttonWidth = 100;
        const unsigned int buttonHeight = 40;
        const std::string buttonLabel = "Click me!";
        TDT4102::Button button {buttonPosition, buttonWidth, buttonHeight, buttonLabel};

        TDT4102::AnimationWindow window;
        window.add(button);
        window.wait_for_close();

        return 0;
    }

Vi kan nå definere callback funksjonen. Den skal ha ingen parametere, og returnere `void` *. Det er mulig å definere flere callback funksjoner i en enkelt fil, og navnet på funksjonene er ikke viktig. Flere GUI elementer kan ha samme callback funksjonen.

- Det er mulig å bruke lambdas til å komme seg rundt denne begrensingen, men lambdas er ikke pensum i dette faget.

    #include "AnimationWindow.h"
    #include "widgets/Button.h"

    void callbackFunction() {
        std::cout << "This is printed when this function is executed." << std::endl;
    }

    int main() {
        const TDT4102::Point buttonPosition {100, 100};
        const unsigned int buttonWidth = 100;
        const unsigned int buttonHeight = 40;
        const std::string buttonLabel = "Click me!";
        TDT4102::Button button {buttonPosition, buttonWidth, buttonHeight, buttonLabel};

        TDT4102::AnimationWindow window;
        window.add(button);
        window.wait_for_close();

        return 0;
    }

Den siste steget er å bruke `callbackFunction()` funksjonen som callback av knappen vi laget tidligere. Vi gjør dette ved å bruke `setCallback()` funksjonen, med navn på funksjonen som vi ønsker å bruke som parameter:

    #include "AnimationWindow.h"
    #include "widgets/Button.h"

    void callbackFunction() {
        std::cout << "This is printed when this function is executed." << std::endl;
    }

    int main() {
        const TDT4102::Point buttonPosition {100, 100};
        const unsigned int buttonWidth = 100;
        const unsigned int buttonHeight = 40;
        const std::string buttonLabel = "Click me!";
        TDT4102::Button button {buttonPosition, buttonWidth, buttonHeight, buttonLabel};

        TDT4102::AnimationWindow window;
        button.setCallback(callbackFunction);
        window.add(button);
        window.wait_for_close();

        return 0;
    }

Når vi kjører dette programmet og klikker på knappen ser vi at callback funksjonen skriver ut tekst på skjermen:

### Objekt-orienterte callback funksjoner

Dersom man ønsker å sette callback-funksjonen til en klassemetode må man bruke `std::bind` for å gjøre funksjonen kompatibel.

    class SuperManWindow : public TDT4102::AnimationWindow {
       public:
        SuperManWindow();
        void superCallbackFunction();
        void fly();
       private:
        TDT4102::Button flyButton;
    };

    void SuperManWindow::superCallbackFunction() {
        std::cout << "Thanks for clicking, im going to fly\n";
        this->fly();
        // Do something else
    }

    SuperManWindow::SuperManWindow()
        : TDT4102::AnimationWindow(50, 50, 500, 500, "Super man window"),
        flyButton{{100, 100}, 50, 50, "fly"} {
        // Here we convert the class method to a void function
        // by binding this instance of the class to the function
        flyButton.setCallback(std::bind(&SuperManWindow::superCallbackFunction, this));
        add(flyButton);
    }

## GUI Elementer

Hvert GUI element som kan brukes i `TDT4102::AnimationWindow` er definert i sin egen klasse som arver fra `TDT4102::Widget`. GUI elementer som finnes i AnimationWindow står i tabellen nedenfor med en liten forklaring.

Widget | Forklaring
`TDT4102::Button` | Knapp som kan trykkes på
`TDT4102::TextBox` | Tekstfelt som ikke kan endres av bruker
`TDT4102::TextInput` | Tekstfelt som bruker kan endre
`TDT4102::DropdownList` | Liste over ulike alternativer
`TDT4102::Checkbox` | Checkbox for å velge et alternativ blant flere
`TDT4102::RadioButton` | Radiobutton for å velge et alternativ fra en gruppe
`TDT4102::Slider` | Slider for å velge en verdi i et intervall

Hvis du for eksempel ønsker å vise en knapp i vinduet, lager du en instans av `TDT4102::Button` klassen og legger den til i vinduet ved å bruke medlemsfunksjonen:

    void TDT4102::AnimationWindow::add(TDT4102::Widget &widgetToAdd);

Hvis du ikke ønsker å vise knappen med en gang, kan du kalle på funksjonen `setVisible(bool isVisible)` med `false` på knapp-variabelen for å skjule elementet. Bruk `true` for å vise elementet.

### Knapp

En knapp er et GUI element som gjør noe når du klikker på den:

Her er et eksempel som vises hvordan den brukes:

    #include "AnimationWindow.h"
    #include "widgets/Button.h"

    void buttonClicked() {
        std::cout << "Someone clicked on me!" << std::endl;
    }

    int main() {
        const TDT4102::Point buttonPosition {100, 100};
        const unsigned int buttonWidth = 100;
        const unsigned int buttonHeight = 40;
        const std::string buttonLabel = "Click me!";
        TDT4102::Button button {buttonPosition, buttonWidth, buttonHeight, buttonLabel};

        TDT4102::AnimationWindow window;
        window.add(button);
        button.setCallback(buttonClicked);
        button.setButtonColor(TDT4102::Color::silver);
        window.wait_for_close();
        return 0;
    }

Merk at vi må først inkludere headeren til `TDT4102::Button` klassen ved å skrive `#include "widgets/Button.h"` øverst i filen.

Deretter lager vi en knapp ved å instansiere `TDT4102::Button` klassen, som har følgende konstruktør:

    TDT4102::Button(TDT4102::Point location, unsigned int width, unsigned int height, std::string label);

Her definerer `TDT4102::Point location` hvor knappen skal plasseres på skjermen, `width` og `height` størrelsen, og `label` teksten som skal vises på knappen. Vi bruker `add()` og `setCallback()` funksjonene for å gjøre den synlig i vinduet, og at det skjer noe når vi klikker på den.

Callback funksjonen blir kalt hver gang brukeren klikker på knappen.

Buttonklassen har diverse funksjoner for å endre på utseende. De andre widgetsene har tilsvarende funksjoner unntatt `DropdownList`.

    void setLabel(std::string newLabel);
    void setLabelColor(TDT4102::Color newColor);
    void setButtonColor(TDT4102::Color newColor);
    void setButtonColorHover(TDT4102::Color newColor);
    void setButtonColorActive(TDT4102::Color newColor);

Du kan endre tekst og tekstfarge med `setLabel` og `setLabelColor`. Du kan endre fargen for knappen med `setButtonColor`, fargen når du har musepekeren over med `setButtonColorHover` og fargen på knappen når den er trykket ned med `setButtonColorActive`.

### Tekstfelt

Et tekstfelt er et GUI element hvor man kan skrive inn tekst. Det er også mulig å hente ut teksten som er i tekstfelet med funksjonen `TDT4102::TextInput::getText()`. For å skrive til et tekstfelt bruker vi funksjonen `TDT4102::TextInput::setText(std::string text)`.

    #include "AnimationWindow.h"
    #include "widgets/TextInput.h"

    TDT4102::TextInput textField {{100, 100}, 300, 30, "This text field is small!"};

    void textFieldChanged() {
        std::cout << "The text field now contains: " << textField.getText() << std::endl;
    }

    int main() {
        TDT4102::AnimationWindow window;
        window.add(textField);
        textField.setCallback(textFieldChanged);
        window.wait_for_close();
        return 0;
    }

Tekstfeltene kan også være over flere linjer:

    #include "AnimationWindow.h"
    #include "widgets/TextInput.h"

    TDT4102::TextInput textField {{100, 100}, 300, 150, "This text field\ncontains more than\none line of text!"};

    void textFieldChanged() {
        std::cout << "The text field now contains: " << textField.getText() << std::endl;
    }

    int main() {
        TDT4102::AnimationWindow window;
        window.add(textField);
        textField.setCallback(textFieldChanged);
        window.wait_for_close();
        return 0;
    }

### TextBox

TextBox er veldig lik TextInput, den eneste forskjellen er at innholdet ikke kan endres av bruker.

    #include "AnimationWindow.h"
    #include "widgets/TextBox.h"

    TDT4102::TextBox tbox({100, 100}, 300, 30, "Hello there!");

    int main() {
        TDT4102::AnimationWindow window;
        window.add(tbox);
        window.wait_for_close();
        return 0;
    }

### DropdownList

Dropdown-liste har en liste med elementer som man kan velge mellom. Det valgte elementet henter vi ut med funksjonen `TDT4102::DropdownList::getSelectedValue()`. For å endre på elementene i lista, bruk funksjonen `TDT4102::DropdownList::setOptions(std::vector<std::string> &updatedOptionsList)` med en vector som inneholder de nye elementene.

    #include "AnimationWindow.h"
    #include "widgets/DropdownList.h"

    std::vector<std::string> options {"Hello", "There", "General", "You", "Are", "A", "Bold", "One"};
    TDT4102::DropdownList list({100, 100}, 300, 30, options);

    void handle() {
        std::cout << "Selected: " << list.getSelectedValue() << std::endl;
    }

    int main() {
        TDT4102::AnimationWindow window;
        window.add(list);
        list.setCallback(handle);
        window.wait_for_close();
        return 0;
    }

### Slider

En slider lar brukeren velge en verdi innenfor et intervall. Du ser dem bli brukt til å endre volum eller lysstyrke.

Her lager vi en slider i punktet `(100,100)` med bredde lik 200 piksler, høyde lik 30. De neste verdiene bestemmer hvordan slideren oppfører seg; minsteverdien og maksverdien blir satt til hhv. 0 og 50. Neste verdien er initialveriden til slideren og den siste er hvor mye verdien endrer på seg når bruker interagerer med slideren.

    #include "AnimationWindow.h"
    #include "widgets/Slider.h"

    TDT4102::Slider sl{{100,100},200, 30, 0, 50, 0, 2};

    void callbackFunction() {
        std::cout << "Current value is: " << sl.getValue() << std::endl;
    }

    int main() {

        TDT4102::AnimationWindow window;
        window.add(sl);
        sl.setCallback(callbackFunction);
        window.wait_for_close();

        return 0;
    }

### Checkbox

Checkbox'en lar brukeren velge én eller flere uavhengige alternativer. Den brukes ofte i skjemaer, innstillinger og applikasjoner der brukeren gjør valg som ikke utelukker hverandre.

    #include "AnimationWindow.h"
    #include "widgets/CheckBox.h"

    int main() {

        TDT4102::CheckBox cb{{200, 200}, 200, 50, ""};

        cb.setLabelColor(TDT4102::Color::dark_violet);
        cb.setLabel("This is a checkbox");

        TDT4102::AnimationWindow window;
        window.add(cb);

        window.wait_for_close();

        return 0;
    }

### RadioButton

Radiobuttons tillater brukeren til å velge ett alternativ fra en gruppe med forhåndsdefinerte valg. I motsetning til checkboxes, hvor flere valg kan være aktive samtidig, tillater radioknapper kun ett aktivt valg om gangen innenfor samme gruppe (NB! dette må du sjekke for selv).

    #include "AnimationWindow.h"
    #include "widgets/RadioButton.h"

    int main() {

        TDT4102::RadioButton rb{{200, 200}, 200, 30, "Easy"};
        TDT4102::RadioButton rb2{{200, 235}, 200, 50, "Hard"};

        rb.setLabelColor(TDT4102::Color::black);
        rb2.setLabelColor(TDT4102::Color::black);

        TDT4102::AnimationWindow window;
        window.add(rb);
        window.add(rb2);

        window.wait_for_close();
        return 0;
    }