Archives pour la catégorie GUI

Une classe GUIDraggableWidget pour permettre de déplacer une GUIWidget

lnh5ycp0amwjyttfhg4x

Intro :

Parfois nous avons besoin qu’une GUIWidget puisse être déplaçable à volonté tout en restant appuyé dessus la souris.

Prérequis :

– Avoir compris la classe GUIWidget

Contenu :

Voici le contenu GUIDraggableWidget.h :

#ifndef GUI_DRAGGABLE_WIGET_H
#define GUI_DRAGGABLE_WIGET_H

#include <iostream>
#include <d3dx10math.h>

#include "GUIImage.h"
#include "GUIButton.h"

class GUIDraggableWiget : public GUIImage
{
public:
    enum DraggableBoxState
    {
        NON_ACTIVE,
        DRAGGING
    };

    GUIDraggableWiget(const std::string& sName, const std::string& sTextureFileName);
    virtual ~GUIDraggableWiget();

    virtual void Update(float fTimeSinceLastFrame);

    virtual void SetPosition(unsigned int x, unsigned int y);
    virtual void SetSize(unsigned int iWidth, unsigned int iHeight);

    void DisableDragging();
    void EnableDragging();

private:
    bool IsCollidingWithDraggableBox(int x, int y);
    bool IsCollidingWithCloseBox(int x, int y);

    void SetBoxesPositions(unsigned int x, unsigned int y);

    D3DXVECTOR2 GetDraggableBoxPosition();

    DraggableBoxState GetDraggableBoxState();
    void SetDraggableBoxState(DraggableBoxState state);

    void SetImageSize(unsigned int iWidth, unsigned int iHeight);

private:
    AABB m_draggableBoxAABB;
    AABB m_closeBoxAABB;

    DraggableBoxState m_state;

    int xPos;
    int yPos;

    int sx;
    int sy;

    bool m_bDragging;
    bool m_bDraggingEnabled;
};

#endif

 

Voici le fichier GUIDraggableWidget.cpp :

#include "GUIDraggableWidget.h"
#include "InputManager.h"
#include "GUIManager.h"

GUIDraggableWiget::GUIDraggableWiget(const std::string& sName, const std::string& sTextureFileName) :
GUIImage(sName, sTextureFileName),
xPos(0),
yPos(0),
sx(0),
sy(0),
m_bDragging(false),
m_bDraggingEnabled(true)
{
    ZeroMemory(&m_draggableBoxAABB, sizeof(AABB));
    ZeroMemory(&m_closeBoxAABB, sizeof(AABB));

    m_draggableBoxAABB.w = (float)GetImage()->GetWidth();
    m_draggableBoxAABB.h = (float)GetImage()->GetHeight() * 0.3f;    

    m_closeBoxAABB.w = 20;
    m_closeBoxAABB.h = 20;    
}

GUIDraggableWiget::~GUIDraggableWiget()
{
}

void GUIDraggableWiget::Update(float fTimeSinceLastFrame)
{
    if (!IsVisible())
    {
        return;
    }

    GUIImage::Update(fTimeSinceLastFrame);

    int absX, absY;
    INPUT_MANAGER->GetAbsMouseLocation(absX, absY);

    int dX = absX - sx;
    int dY = absY - sy;

    if (INPUT_MANAGER->IsMouseButtonDown(MOUSE_LEFT_BUTTON_DOWN)
        && GUI_MANAGER->DoesWidgetIsOnFront(this)
        && SYSTEM->HasFocus() && IsActive())
    {
        if (IsCollidingWithDraggableBox(absX, absY) && m_bDraggingEnabled)
        {
             m_bDragging = true;
        }

        if (IsCollidingWithCloseBox(absX, absY))
        {
            SetVisible(false);
        }

        if (m_bDragging)
        {
            SetDraggableBoxState(DraggableBoxState::DRAGGING);

            xPos += dX;
            yPos += dY;

            /* Contraintes de bords */
            if (yPos <= 0)
            {
                yPos = 0;
            }

            if (xPos <= 0)
            {
                xPos = 0;
            }

            unsigned int iWidthOffset = D3D10_RENDERER->GetViewportWidth() - GetImage()->GetWidth();
            unsigned int iHeightOffset = D3D10_RENDERER->GetViewportHeight() - GetImage()->GetHeight();

            /* Contraintes de bords */
            if (xPos >= iWidthOffset)
            {
                xPos = iWidthOffset;
            }

            if (yPos >= iHeightOffset)
            {
                yPos = iHeightOffset;
            }    
            
            SetPosition(xPos, yPos);
        }
    }
    else
    {
        m_bDragging = false;
    }

    sx = absX;
    sy = absY;
}

bool GUIDraggableWiget::IsCollidingWithDraggableBox(int x, int y)
{
    return (x >= m_draggableBoxAABB.x) && (x < (m_draggableBoxAABB.x + m_draggableBoxAABB.w))
        && (y >= m_draggableBoxAABB.y) && (y < (m_draggableBoxAABB.y + m_draggableBoxAABB.h))
        && IsVisible();
}

bool GUIDraggableWiget::IsCollidingWithCloseBox(int x, int y)
{
    return (x >= m_closeBoxAABB.x) && (x < (m_closeBoxAABB.x + m_closeBoxAABB.w))
        && (y >= m_closeBoxAABB.y) && (y < (m_closeBoxAABB.y + m_closeBoxAABB.h))
        && IsVisible();
}

void GUIDraggableWiget::SetDraggableBoxState(DraggableBoxState state)
{
    m_state = state;
}
    
GUIDraggableWiget::DraggableBoxState GUIDraggableWiget::GetDraggableBoxState()
{
    return m_state;
}

void GUIDraggableWiget::SetBoxesPositions(unsigned int x, unsigned int y)
{  
    m_draggableBoxAABB.x = x;
    m_draggableBoxAABB.y = y;

    m_closeBoxAABB.x = (GetImage()->GetWidth() - m_closeBoxAABB.w) + x;
    m_closeBoxAABB.y = y;
}

D3DXVECTOR2 GUIDraggableWiget::GetDraggableBoxPosition()
{  
    return D3DXVECTOR2(m_draggableBoxAABB.x, m_draggableBoxAABB.y);
}

void GUIDraggableWiget::SetPosition(unsigned int x, unsigned int y)
{  
    SetBoxesPositions(x, y);

    xPos = x;
    yPos = y;

    GUIImage::SetPosition(x, y);
}

void GUIDraggableWiget::SetSize(unsigned int iWidth, unsigned int iHeight)
{    
    GUIImage::SetSize(iWidth, iHeight);

    m_draggableBoxAABB.w = (float)GetImage()->GetWidth();
    m_draggableBoxAABB.h = (float)GetImage()->GetHeight() * 0.3f;    
}

void GUIDraggableWiget::DisableDragging()
{
    m_bDraggingEnabled = false;
}

void GUIDraggableWiget::EnableDragging()
{
    m_bDraggingEnabled = true;
}

 

Résumé :

Nous avons décrit une classe qui représente un moyen d’afficher une GUIWidget déplaçable.

Une classe GUIButton pour représenter un bouton cliquable

8HcZT

Intro :

Parfois nous avons besoin d’afficher un bouton pour rendre l’interface utilisateur (GUI) plus dynamique. Le bouton peut afficher une autre image lorsqu’il est reste cliqué dessus !

Prérequis :

– Avoir lu la classe GUIWidget

Contenu :

Voici le fichier GUIButton.h :

#ifndef GUI_BUTTON_H
#define GUI_BUTTON_H

#include <d3dx10math.h>
#include <iostream>

#include "Sprite2D.h"
#include "GUIImage.h"
#include "Defines.h"

class GUIButton : public GUIImage
{
public:
    enum ButtonState
    {
        NON_ACTIVE,
        HOVER,
        CLICKED,
        PUSHED_OUT
    };

    GUIButton(const std::string& sName, const std::string& sTextureName, const std::string& sTextureClickedName = NONE,
        const std::string& sTextureHoverName = NONE);

    virtual ~GUIButton();

    virtual void Update(float fTimeSinceLastFrame);

    ButtonState GetState();
    void SetState(ButtonState state);

    void SetHoverOnImage();
    void SetClickedOnImage();
    void SetNormalImage();

    bool IsClickedOn();
    bool IsPushedOut();
    bool IsNonActive();

private:
    ButtonState m_state;

    std::string m_sNormalTextureFile;
    std::string m_sClickedOnTextureFile;
    std::string m_sHoverOnTextureFile;

    bool m_bClickedOn;
    bool m_bHover;
    bool m_bHasClickedOnImage;
    bool m_bHasHoverdOnImage;

    bool m_bMouseButtonReleased;
};

#endif

 

Voici le fichier GUIButton.cpp :

#include "GUIButton.h"
#include "InputManager.h"
#include "GUIManager.h"
#include "Utils.h"

GUIButton::GUIButton(const std::string& sName, const std::string& sTextureName, const std::string& sTextureClickedName,
                     const std::string& sTextureHoverName) :
GUIImage(sName, sTextureName),
m_state(NON_ACTIVE),
m_sNormalTextureFile(sTextureName),
m_sClickedOnTextureFile(sTextureClickedName),
m_sHoverOnTextureFile(sTextureHoverName),
m_bClickedOn(false),
m_bHover(false),
m_bMouseButtonReleased(false),
m_bHasClickedOnImage(false),
m_bHasHoverdOnImage(false)
{
    // Image quand on appuie
    if (Utils::IsFileReadable(sTextureClickedName))
    {
        m_bHasClickedOnImage = true;
    }

    // Image quand on survole
    if (Utils::IsFileReadable(sTextureHoverName))
    {
        m_bHasHoverdOnImage = true;
    }
}

GUIButton::~GUIButton()
{
}

void GUIButton::Update(float fTimeSinceLastFrame)
{
    if (!IsVisible())
    {
        return;
    }

    int x, y;
    INPUT_MANAGER->GetAbsMouseLocation(x, y);
    
    if (IsActive())
    {
        if (IsCollidingWithMousePointer(x, y)
            && GUI_MANAGER->IsWidgetFirstOverlapping(this))
        {
            SetState(ButtonState::HOVER);
            m_bHover = true;

            SetHoverOnImage();

            if (INPUT_MANAGER->IsMouseButtonDown(MOUSE_LEFT_BUTTON_DOWN))
            {
                m_bClickedOn = true;

                SetState(ButtonState::CLICKED);

                GUIParameters param;
                param << GetState();

                SendEventToListeners(param);

                SetClickedOnImage();
            }
            else if (m_bClickedOn)
            {
                SetNormalImage();    

                m_bClickedOn = false;

                SetState(ButtonState::PUSHED_OUT);
            }
        }
        else if (m_bHover)
        {
            SetState(ButtonState::NON_ACTIVE);

            SetNormalImage();

            m_bHover = false;
        }
        else
        {
            m_bClickedOn = false;
        }
    }

    GUIImage::Update(fTimeSinceLastFrame);
}

GUIButton::ButtonState GUIButton::GetState()
{
    return m_state;
}

void GUIButton::SetState(ButtonState state)
{
    m_state = state;
}

bool GUIButton::IsClickedOn()
{
    return GetState() == GUIButton::ButtonState::CLICKED && IsVisible();
}

bool GUIButton::IsPushedOut()
{
    return GetState() == GUIButton::ButtonState::PUSHED_OUT && IsVisible();
}

bool GUIButton::IsNonActive()
{
    return GetState() == GUIButton::ButtonState::NON_ACTIVE && IsVisible();
}

void GUIButton::SetClickedOnImage()
{
    if (m_bHasClickedOnImage)
    {
        SetTextureImage(m_sClickedOnTextureFile);
    }
}

void GUIButton::SetNormalImage()
{
    SetTextureImage(m_sNormalTextureFile);
}

void GUIButton::SetHoverOnImage()
{
    if (m_bHasHoverdOnImage)
    {
        SetTextureImage(m_sHoverOnTextureFile);
    }
}

 

Résumé :

Voici une classe qui permet d’afficher un bouton cliquable.

Une classe GUIImage pour afficher une image

Intro :

4NgHv

Parfois nous avons besoin d’afficher une image pour rendre l’interface utilisateur (GUI) plus jolie.

Prérequis :

– Avoir lu la classe GUIWidget

Contenu :

Voici le fichier GUIImage.h :

#ifndef GUI_IMAGE_H
#define GUI_IMAGE_H

#include "GUIWidget.h"

class GUIImage : public GUIWidget
{
public:
    GUIImage(const std::string& sName, const std::string& sTextureFileName);
    virtual ~GUIImage();

    virtual void SetTextureImage(const std::string& sTextureName);

    virtual void Update(float fTimeSinceLastFrame);

    virtual void SetPosition(unsigned int x, unsigned int y);

    virtual void SetSize(unsigned int iWidth, unsigned int iHeight);

    virtual void SetVisible(bool bVisible);

    Sprite2D* GetImage();

private:
    Sprite2D* m_pImage;
};

#endif

 

Voici le fichier GUIImage.cpp :

#include "GUIImage.h"
#include "Sprite2D.h"
#include "Defines.h"

GUIImage::GUIImage(const std::string& sName, const std::string& sTextureFileName) :
GUIWidget(sName),
m_pImage(nullptr)
{
    m_pImage = new Sprite2D(sTextureFileName);
    m_pImage->Initialize();

    unsigned int iImageWidth = m_pImage->GetWidth();
    unsigned int iImageHeight = m_pImage->GetHeight();

    SetSize(iImageWidth, iImageHeight);
}

GUIImage::~GUIImage()
{
    SAFE_DELETE(m_pImage);
}

void GUIImage::Update(float fTimeSinceLastFrame)
{
    if (IsVisible())
    {
        m_pImage->Render();
    }

    GUIWidget::Update(fTimeSinceLastFrame);
}

void GUIImage::SetTextureImage(const std::string& sTextureName)
{
    m_pImage->SetTextureImage(sTextureName);

    // On restaure les dimensions d'avant
    unsigned int iWidth = 0;
    unsigned int iHeight = 0;

    GetSize(iWidth, iHeight);

    m_pImage->SetImageSize(iWidth, iHeight);
}

void GUIImage::SetPosition(unsigned int x, unsigned int y)
{
    m_pImage->SetPosition(x, y);

    GUIWidget::SetPosition(x, y);
}

void GUIImage::SetSize(unsigned int iWidth, unsigned int iHeight)
{
    m_pImage->SetImageSize(iWidth, iHeight);

    GUIWidget::SetSize(iWidth, iHeight);
}

void GUIImage::SetVisible(bool bVisible)
{
    if (bVisible)
    {
        m_pImage->Show();
    }
    else
    {
        m_pImage->Hide();
    }

    GUIWidget::SetVisible(bVisible);
}

Sprite2D* GUIImage::GetImage()
{
    return m_pImage;
}

Résumé :

Une classe GUIWidget pour gérer les objets graphiques

Morrowind_UI

Intro :

Dans un jeu vidéo, nous avons besoin de créer des interfaces graphiques afin que le joueur puisse communiquer ou sélectionner des items, cliquer sur des boutons, déplacer une armure, etc…

Prérequis :

– Savoir initialiser DirectX 10

– Savoir lire du C++

– Connaître la classe Sprite2D

Contenu :

Voici le fichier GUIWidget.h  :

#ifndef GUI_WIDGET_H
#define GUI_WIDGET_H

#include <iostream>
#include <vector>

#include <d3dx10math.h>
#include "DataParameters.h"

class Sprite2D;

typedef DataParameters GUIParameters; 

struct AABB
{
    unsigned int x;
    unsigned int y;
    unsigned int w;
    unsigned int h;
};

class GUIEventListener
{
public:
    GUIEventListener() {}
    virtual ~GUIEventListener() {}

    virtual void OnGUIEvent(GUIParameters& param) = 0;
};

class GUIWidget
{
public:
    GUIWidget(const std::string& sName);
    virtual ~GUIWidget();

    virtual void Update(float fTimeSinceLastFrame);

    D3DXVECTOR2 GetPosition();
    virtual void SetPosition(unsigned int x, unsigned int  y);
    void SetRelativePosition(unsigned int  x, unsigned int  y);

    bool IsCollidingWithMousePointer(unsigned int x, unsigned int y);

    void SetVisible(bool bVisible);
    bool IsVisible();

    void AddEventListener(GUIEventListener* pEventListener);

    virtual void SetSize(unsigned int iWidth, unsigned int iHeight);
    void GetSize(unsigned int& iWidth, unsigned int& iHeight);

    std::string GetName();

    void AddChild(GUIWidget* pChild);
    void SetParent(GUIWidget* pParent);

    bool HasParent();

    void SetCanBeOverlaped(bool bCanBeOverlaped);
    bool CanOverlap();

    void GetChildren(std::map<std::string, GUIWidget*>& children);
    GUIWidget* GetParent();

    bool HasChildren();

    virtual void SetActive(bool bActive);
    bool IsActive();

    bool IsChildrenLocked();
    void SetChildrenLocked(bool bActive);

protected:
    std::vector<GUIEventListener*> m_eventListeners;

    void SendEventToListeners(GUIParameters& param);

private:
    AABB m_aabox;

    bool m_bVisible;

    unsigned int m_iPosX;
    unsigned int m_iPosY;

    std::string m_sName;

    GUIWidget* m_pParent;

    std::map<std::string, GUIWidget*> m_children;

    bool m_bCanBeOverlaped;
    bool m_bActive;
    bool m_bIsChildrenLocked;
};

#endif

 

Voici le fichier GUIWidget.cpp :

#include "GUIWidget.h"

#include "Sprite2D.h"
#include "Defines.h"

#include "GUIManager.h"

#include "System.h"
#include "InputManager.h"
#include "GUIManager.h"

GUIWidget::GUIWidget(const std::string& sName) :
m_iPosX(0),
m_iPosY(0),
m_sName(sName),
m_pParent(nullptr),
m_bCanBeOverlaped(true),
m_bVisible(true),
m_bActive(true),
m_bIsChildrenLocked(false)
{
    ZeroMemory(&m_aabox, sizeof(AABB));
}

GUIWidget::~GUIWidget()
{
}

void GUIWidget::Update(float fTimeSinceLastFrame)
{
}

void GUIWidget::SetCanBeOverlaped(bool bCanBeOverlaped)
{
    m_bCanBeOverlaped = bCanBeOverlaped;

    if (HasChildren())
    {
        for (auto it = m_children.begin(); it != m_children.end(); it++)
        {
            GUIWidget* pChild = it->second;

            pChild->SetCanBeOverlaped(bCanBeOverlaped);
        }
    }
}

bool GUIWidget::CanOverlap()
{
    return m_bCanBeOverlaped;
}

std::string GUIWidget::GetName()
{
    return m_sName;
}

D3DXVECTOR2 GUIWidget::GetPosition()
{
    return D3DXVECTOR2(m_iPosX, m_iPosY);
}

void GUIWidget::SetPosition(unsigned int x, unsigned int  y)
{
    m_iPosX = x;
    m_iPosY = y;

    m_aabox.x = x;
    m_aabox.y = y;
}

bool GUIWidget::IsCollidingWithMousePointer(unsigned int x, unsigned int  y)
{
    return (x >= m_aabox.x) && (x < (m_aabox.x + m_aabox.w))
        && (y >= m_aabox.y) && (y < (m_aabox.y + m_aabox.h));
}

void GUIWidget::SetVisible(bool bVisible)
{
    m_bVisible = bVisible;

    if (HasChildren())
    {
        for (auto it = m_children.begin(); it != m_children.end(); it++)
        {
            GUIWidget* pChild = it->second;

            pChild->SetVisible(bVisible);

            if (!bVisible)
            {
                SetActive(false);
            }
        }
    }
}

bool GUIWidget::IsVisible()
{
    return m_bVisible;
}

void GUIWidget::AddEventListener(GUIEventListener* pEventListener)
{
    m_eventListeners.push_back(pEventListener);
}

void GUIWidget::SendEventToListeners(GUIParameters& param)
{
    for (unsigned int i = 0; i < m_eventListeners.size(); i++)
    {
        GUIEventListener* pEventListener = m_eventListeners[i];
        pEventListener->OnGUIEvent(param);
    }
}

void GUIWidget::SetSize(unsigned int iWidth, unsigned int iHeight)
{
    m_aabox.w = iWidth;
    m_aabox.h = iHeight;
}

void GUIWidget::GetSize(unsigned int& iWidth, unsigned int& iHeight)
{
    iWidth = m_aabox.w;
    iHeight = m_aabox.h;
}

void GUIWidget::AddChild(GUIWidget* pChild)
{
    pChild->SetParent(this);

    m_children[pChild->GetName()] = pChild;
}

void GUIWidget::SetParent(GUIWidget* pParent)
{
    m_pParent = pParent;
}

void GUIWidget::GetChildren(std::map<std::string, GUIWidget*>& children)
{
    children = m_children;
}

GUIWidget* GUIWidget::GetParent()
{
    return m_pParent;
}

bool GUIWidget::HasParent()
{
    return m_pParent != nullptr;
}

void GUIWidget::SetRelativePosition(unsigned int  x, unsigned int  y)
{
    if (HasParent())
    {
        GUIWidget* pParent = GetParent();

        D3DXVECTOR2 pos = pParent->GetPosition();

        SetPosition(pos.x + x, pos.y + y);
    }
}

bool GUIWidget::HasChildren()
{
    return m_children.size() > 0;
}

void GUIWidget::SetActive(bool bActive)
{
    if (HasChildren())
    {
        for (auto it = m_children.begin(); it != m_children.end(); it++)
        {
            GUIWidget* pChild = it->second;

            pChild->SetActive(bActive);
        }
    }

    m_bActive = bActive;
}

bool GUIWidget::IsActive()
{
    return m_bActive;
}

bool GUIWidget::IsChildrenLocked()
{
    return m_bIsChildrenLocked;
}

void GUIWidget::SetChildrenLocked(bool bActive)
{
    m_bIsChildrenLocked = bActive;
}

Résumé :

Nous avons présenter la première classe nommée GUIWidget qui servira de base à toutes les autres classes graphiques (GUI).

Une console Quake-like colorisée

quake-like_console

Intro :

Parfois dans un jeu on a besoin de configurer ou de paramétrer le gameplay ou les mécanismes du jeu directement dans le programme.

Prérequis :

– Savoir un peu utiliser DirectX 10

– Savoir lire du C++

Explications :

On peut entrer et exécuter des commandes (toutes très utiles) pour modifier les variables ou les comportements du jeu. On peut aussi par ce biais afficher des informations système ou des informations sur le comportement du jeu.

Nous allons implémenter cette console dans DirectX 10. Mais vous pouvez aussi l’implémenter dans votre propre application à partir du code expliqué.

Cette console présente les fonctionnalités suivantes :

– éléments de texte en couleur
– historique de commande
– commandes basiques
– image de fond
– correction / suppression de caractère de la ligne de commande

Prérequis :

– Savoir lire du C++

– Savoir initialiser DirectX 10

Explications :

Voici le fichier Console.h :

#ifndef CONSOLE_H
#define CONSOLE_H

#include <string>
#include <vector>
#include <list>
#include <d3dx10math.h>

#include "Singleton.h"
#include "Sprite2D.h"
#include "InputManager.h"

class KeyEvent;
class Line;
class Command;
class DataParameters;

struct MessageType
{
    enum Type {MSG_SYSTEM, MSG_WARNING, MSG_ERROR, MSG_DEBUG, MSG_GAME, MSG_COMMAND, MSG_BLANK};

    // Type
    static std::string GetMessageType(MessageType::Type type);

    // Couleur
    static D3DXCOLOR GetMessageColour(MessageType::Type type);
};

/* Un ensemble de mots */
class TextElement
{
public:
    TextElement(std::wstring sTextElementText, D3DXCOLOR colour, bool bBlinking);
    virtual ~TextElement();

    int GetLength();

    std::wstring GetText();

    D3DXCOLOR GetColour();

    void SetColour(D3DXCOLOR colour);

    bool IsBlinking();

private:
    std::wstring m_sTextElement;

    bool m_bBlinking;
    D3DXCOLOR m_colour;    
};

/* Un ensemble de TextElement */
class Line
{
public:
    Line(MessageType::Type type);
    virtual ~Line();

    void AddTextElement(std::wstring sTextElement, D3DXCOLOR colour, bool bBlinking);
    void AddTextElementSolo(std::wstring sTextElement, D3DXCOLOR colour, bool bBlinking);

    void GetTextElements(std::vector<TextElement*>& elems);

    unsigned int GetTextElementsCount();

    MessageType::Type GetType();

private:
    void ParseLines(std::wstring sText, std::vector<std::wstring>& moreLines);

private:
    std::vector<TextElement*> m_textElements;

    MessageType::Type m_type;

    unsigned int m_iLastElementLength;
};

/* Le petit texte de saisie en bas à gauche de la console */
class CommandLinePrompt
{
public:
    CommandLinePrompt();
    virtual ~CommandLinePrompt();

    void AppendCommandLineText(const std::wstring sText);

    void SetText(const std::wstring sText);

    void Clear();

    bool HasText();

    // Retourne le texte du prompt
    const std::wstring GetPromptText();
    // Retourne le texte de la ligne de commande
    const std::wstring GetCommandLineText();

    void Render();

    void SetArrowTransparency(float fValue);
    void SetArrowPosition(float x, float y);
    void SetArrowSize(unsigned int iArrowWidth, unsigned int iArrowHeight);

    void MoveCursorToLeft();
    void MoveCursorToRight();

    unsigned int GetCursorPos();

    void Update(float fYPosition);

    float GetCommandLengthPosition();

    void ResetCursorPos();

private:
    std::wstring m_sPromptText;
    std::wstring m_sCommandLineText;

    Sprite2D* m_pArrowSprite;

    int m_iCursorPos;
};

/* L'historique des commandes */
class CommandsHistory
{
public:
    CommandsHistory();
    virtual ~CommandsHistory();

    bool Empty();

    const std::wstring GetNext();
    const std::wstring GetPrevious();

    void Clear();

    void AddCommand(const std::wstring sCommandName);

private:
    std::list<std::wstring> m_history;

    std::wstring m_sLastCommand;
};

class Console : public Singleton<Console>, public KeyListener
{
    friend class Line;
    friend class CommandLinePrompt;

public:
    enum ConsoleState {MOVING_UP, MOVING_DOWN, HIDDEN, SHOWN};
    enum TransparencyState {FADE_IN, FADE_OUT};

    Console();
    virtual ~Console();
    
    void Initialize(); 

    // Fonctions de conversion de chaînes de caractères
    static std::string ToString(const std::wstring& sText);
    static std::wstring ToWString(const std::string& sText);

    void Render(float fTimeElapsedSinceLastFrame);

    void SetState(ConsoleState state);

    /** Méthodes d'affichage **/
    void Print(Line* pLine);
    void Print(const std::wstring& sMessage, D3DXCOLOR colour, MessageType::Type type);
    void Print(const std::wstring& sMessage, MessageType::Type type);
    void Print(const std::wstring& sMessage, D3DXCOLOR colour);
    void Print(const std::wstring& sMessage);
    void Print(const std::string& sMessage);

    // Ajoute une ligne vide
    void AddBlankLine();

    bool IsVisible();

    void ToggleState();

    void AddToHistory(const std::wstring& cmd);
    bool IsCharAuthorized(const wchar_t key);

    // Ajoute une ligne dans la console
    void AddLine(Line* pLine); 

    // Ces méthodes permettent de défiler la console
    void IncreaseScrollingPosition();
    void DescreaseScrollingPosition();
    void ResetScrollingPosition();

    // Ajoute les éléments qui qui changent de transparence
    void AddBlinkingTextElement(TextElement* pTextElement);
    void RemoveAllBlinkingTextElements();

    void Clear();

    virtual void SoloKeyPressed(const KeyEvent &arg);
    virtual void SoloKeyReleased(const KeyEvent &arg);

    virtual void RepetitiveKeyPressed(const KeyEvent &arg);
    virtual void RepetitiveKeyReleased(const KeyEvent &arg);

    unsigned int GetCharactWidth();

private:
    unsigned int GetWidth();
    float GetStringLength(std::wstring sText);
    void UpdateSprites(float fTimeElapsedSinceLastFrame);
    void CreateTextFont();
    void UpdateCommandLineText();
    void LoadDefaultCommands();
    void TabComplete();
    void GetCommandLineArguments(const std::wstring& sCommandLine, DataParameters& param);
    bool TextIsOutOfWidth(const std::wstring sText);

    void DeletePreviousCharacter();
    void DeleteNextCharacter();

private:
    // Les lignes affichées
    std::vector<Line*> m_lines;

    // Hauteur de la console
    float m_fConsoleHeight;
    // Largeur de la console
    float m_fConsoleWidth;
    
    // Hauteur totale de la console en prenant en compte le prompt
    unsigned int m_iTotalConsoleHeight;

    // Largeur d'un caractère
    float m_fCharWidth;
    // Hauteur d'un caractère
    float m_fCharHeight;

    // Le nombre maximum de lignes affichées à la fois
    unsigned int m_iMaxLinesDisplayed;
    // Le nombre maximul de caractères présent sur une ligne
    unsigned int m_iMaxCharOnLine;

    std::vector<TextElement*> m_blinkingTextElements;

    TransparencyState m_elemState;
       ConsoleState m_state;

    int m_iLinesStart;
    int m_iLinesOffset;

    ID3DX10Font* m_pFont;

    ID3DX10Sprite* m_pConsoleTextSprite;

    Sprite2D* m_pBackgroudImage;
    Sprite2D* m_pBorderImage;

    // Chaîne de tous les carcatères autorisés
    std::wstring m_aLegalChars;

    CommandLinePrompt* m_pPrompt;
    CommandsHistory* m_pHistory;

    float m_fScrollOffset;

    std::list<std::wstring> m_tabHistory;
};

#endif

 

Voici le fichier Console.cpp :

//----------------------------------------------------
// Auteur : Clément Profit
// Date de création : Juillet 2010
// Description : Une console quake-like avec commandes et appel
// d'events
//----------------------------------------------------

#include "InputManager.h"
#include "Console.h"
#include "Defines.h"

#include "Commands.h"
#include "DataParameters.h"
#include "D3D10Renderer.h"

template<> Console* Singleton<Console>::ms_instance = nullptr;

/********** MessageType **********/

std::string MessageType::GetMessageType(MessageType::Type type)
{
    std::string messageType = "";

    switch (type)
    {
        case MSG_SYSTEM: messageType = "[SYSTEM]"; break;
        case MSG_WARNING: messageType = "[WARNING]"; break;
        case MSG_ERROR: messageType = "[SYS_ERROR]"; break;
        case MSG_DEBUG: messageType = "[SYS_DEBUG]"; break;
        case MSG_GAME: messageType = "[GAME]"; break;
        case MSG_COMMAND: messageType = "[COMMAND]"; break;
        case MSG_BLANK: messageType = ""; break;
    }

    return messageType;
}

D3DXCOLOR MessageType::GetMessageColour(MessageType::Type type)
{
    D3DXCOLOR messageColour;;

    switch (type)
    {
        case MSG_SYSTEM: messageColour = D3DXCOLOR(0.8f, 0.9f, 1.0f, 1.0f); break;
        case MSG_WARNING: messageColour = D3DXCOLOR(0.7f, 0.8f, 1.0f, 1.0f); break;
        case MSG_ERROR: messageColour = D3DXCOLOR(0.2f, 0.4f, 1.0f, 1.0f); break;
        case MSG_DEBUG: messageColour = D3DXCOLOR(0.3f, 0.5f, 1.0f, 1.0f); break;
        case MSG_GAME: messageColour = D3DXCOLOR(0.1f, 0.8f, 1.0f, 1.0f); break;
        case MSG_COMMAND: messageColour = D3DXCOLOR(0.4f, 0.8f, 1.0f, 1.0f); break;
        case MSG_BLANK: messageColour = D3DXCOLOR(1.0, 1.0f, 1.0f, 1.0f); break;
    }

    return messageColour;
}

/********** TextElement **********/

TextElement::TextElement(std::wstring sTextElementText, D3DXCOLOR colour, bool bBlinking) :
m_sTextElement(sTextElementText),
m_bBlinking(bBlinking),
m_colour(colour)
{
}

TextElement::~TextElement()
{
}

int TextElement::GetLength()
{
    return m_sTextElement.length();
}

std::wstring TextElement::GetText()
{
    return m_sTextElement;
}

D3DXCOLOR TextElement::GetColour()
{
    return m_colour;
}

void TextElement::SetColour(D3DXCOLOR colour)
{
    m_colour = colour;
}

bool TextElement::IsBlinking()
{
    return m_bBlinking;
}

/********** Line **********/

Line::Line(MessageType::Type type) :
m_type(type),
m_iLastElementLength(0)
{
    // Ajoute le type de message (par ex [Game]) au début de la ligne
    AddTextElementSolo(Console::ToWString(MessageType::GetMessageType(m_type)), MessageType::GetMessageColour(m_type), false);
}

Line::~Line()
{
    for (unsigned int i = 0; i < m_textElements.size(); i++)
    {
        SAFE_DELETE(m_textElements[i]);
    }
}

unsigned int Line::GetTextElementsCount()
{
    return m_textElements.size();
}

/***
Explications : 

Ces deux suivantes instructions servent à faire en sorte qu'un élément de la ligne ne dépasse
la largeur de la console

1 -
2 -

***/
void Line::AddTextElement(std::wstring sTextElement, D3DXCOLOR colour, bool bBlinking)
{
    // Le début de la ligne : [Game]
    std::wstring s = CONSOLE->ToWString(MessageType::GetMessageType( GetType() ));

    unsigned int iStringLength = CONSOLE->GetStringLength( s + sTextElement ) + m_iLastElementLength;

    if (CONSOLE->TextIsOutOfWidth(s + sTextElement)) // || iStringLength > CONSOLE->GetWidth())
    {    
        // Après on segmente la ligne
        std::vector<std::wstring> moreLines;
        ParseLines(sTextElement, moreLines);

        for (unsigned int i = 0; i < moreLines.size(); i++)
        {
            std::wstring pCurrentString = moreLines[i];

            Line* pLine = new Line(m_type);

            pLine->AddTextElementSolo(pCurrentString, colour, bBlinking);

            CONSOLE->AddLine(pLine);

            m_iLastElementLength = 0;
        }    
    }
    else
    {
        AddTextElementSolo(sTextElement, colour, bBlinking);

        // On sauve la longueur en pixel de la dernière ligne affichée
        m_iLastElementLength = CONSOLE->GetStringLength(sTextElement);
    }
}

void Line::AddTextElementSolo(std::wstring sTextElement, D3DXCOLOR colour, bool bBlinking)
{
    TextElement* pTextElement = new TextElement(sTextElement, colour, bBlinking);
    m_textElements.push_back(pTextElement);

    if (bBlinking)
    {
        CONSOLE->AddBlinkingTextElement(pTextElement);
    }
}

// Analyse une ligne et la découpe en plusieurs lignes si le nombre
// de caractères de la ligne passé dépasse un certain seuil
void Line::ParseLines(std::wstring sText, std::vector<std::wstring>& moreLines)
{
    std::wstring s = CONSOLE->ToWString(MessageType::GetMessageType( GetType() ));

    const wchar_t* str = sText.c_str();
    std::wstring line = L"";

    for (unsigned int c = 0; c < sText.length(); c++)
    {    
        if (str1 == '\n' || CONSOLE->TextIsOutOfWidth(s + line))
        {
            moreLines.push_back(line);
            line = L"";
        }

        if (str1 != '\n')
            line += str1;
    }

    if (line.length() > 0)
    {
        moreLines.push_back(line);
    }
}

void Line::GetTextElements(std::vector<TextElement*>& elem)
{
    elem = m_textElements;
}

MessageType::Type Line::GetType()
{
    return m_type;
}

/****************************** CommandLinePrompt ******************************/

CommandLinePrompt::CommandLinePrompt() :
m_sPromptText(CONSOLE_PROMPT_TEXT),
m_pArrowSprite(nullptr),
m_iCursorPos(0)
{
    m_pArrowSprite = new Sprite2D(D3D10_RENDERER->GetDevice(), L"arrow.png", 10, 12);

    m_pArrowSprite->Initialize();
    m_pArrowSprite->SetColor(D3DXCOLOR(1.0f, 0.85f, 0.0f, 0.0f));
}

CommandLinePrompt::~CommandLinePrompt()
{
    delete m_pArrowSprite;
}

void CommandLinePrompt::Update(float fYPosition)
{
    float fPromptXOffset = CONSOLE->GetStringLength(L"_") + CONSOLE->GetStringLength(m_sPromptText);

    float fTotalWidth = GetCommandLengthPosition();

    SetArrowPosition(fPromptXOffset + fTotalWidth, fYPosition);
}

void CommandLinePrompt::AppendCommandLineText(const std::wstring sText)
{
    unsigned int iPromptCursorPos = GetCursorPos();

    std::wstring sCurrentCommandLine = GetCommandLineText();

    std::wstring sFirstPart = sCurrentCommandLine.substr(0, iPromptCursorPos);
    std::wstring sLastPart = sCurrentCommandLine.substr(iPromptCursorPos, sCurrentCommandLine.length());
    
    if (sLastPart.length() > 0)
    {
        sFirstPart += sText;
        m_sCommandLineText = sFirstPart + sLastPart;
        MoveCursorToRight();
    }
    else
    {
        m_sCommandLineText += sText;
        MoveCursorToRight();
    }
}
   
void CommandLinePrompt::SetText(const std::wstring sText)
{
    m_sCommandLineText = sText;
}

void CommandLinePrompt::Clear()
{
    m_sCommandLineText = L"";
    m_iCursorPos = 0;
}

bool CommandLinePrompt::HasText()
{
    return m_sCommandLineText.size() > 0;
}

const std::wstring CommandLinePrompt::GetPromptText()
{
    return m_sPromptText;
}

const std::wstring CommandLinePrompt::GetCommandLineText()
{
    return m_sCommandLineText;
}

void CommandLinePrompt::Render()
{
    m_pArrowSprite->Render();
}

void CommandLinePrompt::SetArrowTransparency(float fValue)
{
    m_pArrowSprite->SetTransparency(fValue);
}

void CommandLinePrompt::SetArrowPosition(float x, float y)
{
    m_pArrowSprite->SetPosition(x, y);
}

void CommandLinePrompt::SetArrowSize(unsigned int iArrowWidth, unsigned int iArrowHeight)
{
    m_pArrowSprite->SetImageSize(iArrowWidth, iArrowHeight);
}

unsigned int CommandLinePrompt::GetCursorPos()
{
    return m_iCursorPos;
}

void CommandLinePrompt::MoveCursorToLeft()
{
    if (m_iCursorPos > 0)
    {
        m_iCursorPos--;
    }
}

void CommandLinePrompt::MoveCursorToRight()
{
    float fPromptXOffset = CONSOLE->GetStringLength(m_sCommandLineText);

    if (GetCommandLengthPosition() < fPromptXOffset)
    {
        m_iCursorPos++;
    }
}

float CommandLinePrompt::GetCommandLengthPosition()
{
    float fTotalWidth = 0.0f;

    for (unsigned int i = 0; i < m_iCursorPos; i++)
    {
        std::wstring s = L"";
        s.push_back(m_sCommandLineText[i]);

        fTotalWidth += CONSOLE->GetStringLength(s);
    } 

    return fTotalWidth;
}

void CommandLinePrompt::ResetCursorPos()
{
    m_iCursorPos = m_sCommandLineText.length();
}

/****************************** CommandsHistory ******************************/

CommandsHistory::CommandsHistory() :
m_sLastCommand(L"")
{
}

CommandsHistory::~CommandsHistory()
{
    m_history.clear();
}

bool CommandsHistory::Empty()
{
    return m_history.size() == 0;
}

const std::wstring CommandsHistory::GetPrevious()
{
    if (!Empty())
    {
        if (m_sLastCommand == m_history.back())
        {
        }

        const std::wstring sCommandName = m_history.back();

        m_history.push_front(sCommandName);
        m_history.pop_back();

        m_sLastCommand = sCommandName;

        return sCommandName;
    }
    else
    {
        return EMPTY;
    }
}

const std::wstring CommandsHistory::GetNext()
{
    if (!Empty())
    {
        if (m_sLastCommand == m_history.front())
        {
        }

        const std::wstring sCommandName = m_history.front();

        m_history.push_back(sCommandName);
        m_history.pop_front();
        
        m_sLastCommand = sCommandName;

        return sCommandName;
    }
    else
    {
        return EMPTY;
    }
}

void CommandsHistory::Clear()
{
    m_history.clear();
}

void CommandsHistory::AddCommand(const std::wstring sCommandName)
{
    m_history.remove(sCommandName);
    m_history.push_back(sCommandName);

    if (m_history.size() > CONSOLE_MAX_COMMAND_HISTORY)
    {
        m_history.pop_front();
    }
}

/****************************** Console ******************************/

Console::Console() :
m_elemState(TransparencyState::FADE_IN),
m_iLinesStart(0),
m_iLinesOffset(0),
m_iMaxCharOnLine(0),
m_pFont(nullptr),
m_pConsoleTextSprite(nullptr),
m_pBackgroudImage(nullptr),
m_pBorderImage(nullptr),
m_aLegalChars(INPUT_LEGAL_CHARS),
m_fCharHeight(0),
m_fCharWidth(0),
m_fScrollOffset(1.0f),
m_pPrompt(nullptr)
{
}

Console::~Console()
{
    RemoveAllBlinkingTextElements();
}

void Console::Initialize()
{
    new CommandsManager();
    CreateTextFont();

    m_fConsoleHeight = (float) D3D10_RENDERER->GetViewportHeight() * CONSOLE_HEIGHT_FACTOR;
    m_fConsoleWidth = (float) D3D10_RENDERER->GetViewportWidth();

    /* Calcul des tailles d'un caractère */
    RECT rect = {0, 0, 0, 0};

    m_pFont->DrawText(nullptr, L"a", -1, &rect, DT_CALCRECT, D3DXCOLOR());

    m_fCharWidth = rect.right - rect.left;
    m_fCharHeight = rect.bottom - rect.top;

    m_iMaxLinesDisplayed = (unsigned int) (m_fConsoleHeight / (m_fCharHeight - CONSOLE_HEIGHT_PADDING / 2));
    m_iMaxCharOnLine = (unsigned int) ((m_fConsoleWidth)  / m_fCharWidth);

    // Le total des lignes affichables + la hauteur du prompt
    m_iTotalConsoleHeight = m_fConsoleHeight + m_fCharHeight + CONSOLE_HEIGHT_PADDING / 2;

    // Image de fond
    m_pBackgroudImage = new Sprite2D(D3D10_RENDERER->GetDevice(), L"background.jpg", m_fConsoleWidth, m_iTotalConsoleHeight);
    m_pBackgroudImage->Initialize();
    m_pBackgroudImage->SetPosition(0, -m_fConsoleHeight);

    // Bord du bas
    m_pBorderImage = new Sprite2D(D3D10_RENDERER->GetDevice(), L"border.png", m_fConsoleWidth, CONSOLE_BORDER_HEIGHT);
    m_pBorderImage->Initialize();

    // La ligne de commande et le prompt
    m_pPrompt = new CommandLinePrompt();
    
    // Ajout de lignes prédéfinies
    for (int i = 0; i < 0; i++)
    {
        Line* line = new Line(MessageType::MSG_GAME);
        //line->AddTextElement(L"En analyse, le nombre dérivé en un point d'une fonction à variable et valeurs réelles est le coefficient directeur de la tangente au graphe de cette fonction en ce point. C'est le coefficient directeur de l'approximation affine de cette fonction en ce point ; ce nombre n'est donc défini que si cette tangente — ou cette approximation — existe.", D3DXCOLOR(0.0f, 1.0f, 1.0f, 1.0f), false);

        line->AddTextElement(std::to_wstring(i), D3DXCOLOR(0.0f, 1.0f, 1.0f, 1.0f), false);

        AddLine(line);
    }

    LoadDefaultCommands();

    Print(L"En mathématiques, une métrique ou distance est une fonction qui définit la distance entre les éléments d'un ensemble. Un ensemble muni d'une distance est appelé un espace métrique. Toute distance induit une topologie sur un ensemble mais la réciproque est fausse : un espace topologique n'est pas toujours métrisable.");

    INPUT_MANAGER->SetKeyListener(this);

    m_pHistory = new CommandsHistory();

    // La console est caché dès le début
    SetState(HIDDEN);

    Print("Salut!");
}

void Console::CreateTextFont()
{
    ID3D10Device* pd3dDevice = D3D10_RENDERER->GetDevice();

    D3DX10CreateSprite(pd3dDevice, 0, &m_pConsoleTextSprite);

    D3DX10_FONT_DESC fd;
    fd.Height = CONSOLE_FONT_HEIGHT;
    fd.Width = CONSOLE_FONT_WIDTH;
    fd.Weight = 3;
    fd.MipLevels = 0;
    fd.Italic = false;
    fd.CharSet = OUT_DEFAULT_PRECIS;
    fd.Quality = ANTIALIASED_QUALITY;
    fd.PitchAndFamily = DEFAULT_PITCH;
    wcscpy(fd.FaceName, CONSOLE_FONT_NAME);

    D3DX10CreateFontIndirect(pd3dDevice, &fd, &m_pFont);
}

float Console::GetStringLength(std::wstring sText)
{
    RECT rect = {0, 0, 0, 0};

    m_pFont->DrawText(nullptr, sText.c_str(), -1, &rect, DT_CALCRECT, D3DXCOLOR());

    float fWidth = rect.right - rect.left;

    // On rajoute les espaces qui ne sont pas pris en compte
    if (!sText.empty() && sText.back() == L' ')
    {
        int c = sText.size() - 1;

        while (true)
        {
            if (sText.at(c) == L' ')
            {
                fWidth += 5;
            }
            else
            {
                break;
            }

            c--;

            // Au cas où...
            if (c < 0)
            {
                break;
            }
        }
    }

    return fWidth;
}

void Console::AddLine(Line* pLine)
{
    // On n'ajoute pas les lignes qui comportent uniquement
    // l'en-tête [Game] par exemple
    if (pLine->GetTextElementsCount() > 1)
    {
        m_lines.push_back(pLine);

        // On décale toutes les lignes
        if (m_lines.size() > m_iMaxLinesDisplayed)
        {
            m_iLinesStart++;
        }
    }
}

void Console::IncreaseScrollingPosition()
{
    if (m_iLinesStart > 0)
    {
        m_iLinesOffset++;
        m_iLinesStart--;
    }
}

void Console::DescreaseScrollingPosition()
{
    if (m_iLinesOffset > 0)
    {
        m_iLinesOffset--;
        m_iLinesStart++;
    }
}

void Console::SetState(ConsoleState state)
{
    m_state = state;
}

void Console::ToggleState()
{
    if (m_state == SHOWN)
    {
        m_state = MOVING_UP;
    }
    else if (m_state == HIDDEN)
    {
        m_state = MOVING_DOWN;
    }
}

void Console::Print(Line* pLine)
{
    AddLine(pLine);
}

void Console::Print(const std::wstring& sMessage, D3DXCOLOR colour, MessageType::Type type)
{
    Line* pLine = new Line(type);

    pLine->AddTextElement(sMessage, colour, false);

    AddLine(pLine);
}

void Console::Print(const std::wstring& sMessage, MessageType::Type type)
{
    Line* pLine = new Line(type);

    pLine->AddTextElement(sMessage, D3DXCOLOR(1.0f, 1.0f,1.0f, 1.0f), false);

    AddLine(pLine);
}

void Console::Print(const std::wstring& sMessage, D3DXCOLOR colour)
{
    Line* pLine = new Line(MessageType::MSG_GAME);

    pLine->AddTextElement(sMessage, colour, false);

    AddLine(pLine);
}

void Console::Print(const std::wstring& sMessage)
{
    Line* pLine = new Line(MessageType::MSG_GAME);

    pLine->AddTextElement(sMessage, D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f), false);

    AddLine(pLine);
}

void Console::Print(const std::string& sMessage)
{
    Line* pLine = new Line(MessageType::MSG_GAME);

    pLine->AddTextElement(ToWString(sMessage), D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f), false);

    AddLine(pLine);
}

void Console::AddBlankLine()
{
    Line* pLine = new Line(MessageType::MSG_BLANK);

    pLine->AddTextElement(L"", D3DXCOLOR(1.0f, 1.0f, 1.0f, 1.0f), false);

    AddLine(pLine);
}

bool Console::IsVisible()
{
    return m_state != HIDDEN;
}

void Console::AddToHistory(const std::wstring& cmd)
{
    m_pHistory->AddCommand(cmd);
}

bool Console::IsCharAuthorized(const wchar_t key)
{
    for (unsigned int c = 0; c < m_aLegalChars.size(); c++)
    {
        if (m_aLegalChars1 == key)
        {
            return true;
        }
    }

    return false;
}

void Console::AddBlinkingTextElement(TextElement* elem)
{
    m_blinkingTextElements.push_back(elem);
}

void Console::RemoveAllBlinkingTextElements()
{
    m_blinkingTextElements.clear();
}

void Console::Clear()
{
    for (unsigned int i = 0; i < m_lines.size(); i++)
    {
        delete m_lines[i];
    }

    m_lines.clear();

    RemoveAllBlinkingTextElements();

    m_iLinesOffset = 0;
    m_iLinesStart = 0;
}

void Console::LoadDefaultCommands()
{
    CONSOLE_COMMANDS_MANAGER->AddCommand( new Command_QUIT() );
    CONSOLE_COMMANDS_MANAGER->AddCommand( new Command_HELP() );
    CONSOLE_COMMANDS_MANAGER->AddCommand( new Command_EXIT() );
    CONSOLE_COMMANDS_MANAGER->AddCommand( new Command_VERSION() );
    CONSOLE_COMMANDS_MANAGER->AddCommand( new Command_CLS() );
}

void Console::TabComplete()
{    
    std::wstring sCurrentCommandLine = m_pPrompt->GetCommandLineText();

    std::vector<std::string> cmds;
    CONSOLE_COMMANDS_MANAGER->GetCommandsNames(cmds);
    
    // On stocke toutes les commandes qui commence par le même début que la saisie
    // et ceci lorsque la liste est vide
    if (sCurrentCommandLine.length() > 0 && m_tabHistory.empty())
    {
        for (int i = 0; i < cmds.size(); i++)
        {
            std::wstring cmd = ToWString(cmds[i]);

            if (cmd.size() > sCurrentCommandLine.size())
            {
                if (cmd.substr(0, sCurrentCommandLine.size()) == sCurrentCommandLine)
                {
                    m_tabHistory.push_back(cmd);
                }
            }
        }        
    }
    // Sinon on stocke toutes les commandes existantes
    else
    {
        for (int i = 0; i < cmds.size(); i++)
        {
            std::wstring cmd = ToWString(cmds[i]);

            m_tabHistory.push_back(cmd);
        }
    }

    // On échange les commandes de manière circulaire dans la liste
    if (!m_tabHistory.empty())
    {
        std::wstring command = m_tabHistory.front();

        m_tabHistory.pop_front();
        m_tabHistory.push_back(command);

        m_pPrompt->SetText(command);
    }
}

void Console::SoloKeyPressed(const KeyEvent& arg)
{
    if (arg.key == DIK_F1)
    {
        ToggleState();
    }

    if (!IsVisible())
    {
        return;
    }

    if (IsCharAuthorized(arg.text))
    {
        std::wstring wStr;
        wStr.push_back( arg.text );

        m_pPrompt->AppendCommandLineText(wStr);

        /* On reset l'historique de selection automatique
        de commande par tabulation */
        m_tabHistory.clear();
    }

    if (arg.key == DIK_RETURN)
    {
        if (!m_pPrompt->HasText())
        {
            return;
        }

        ResetScrollingPosition();

        DataParameters param;
        const std::wstring sCommandLineCaption = m_pPrompt->GetCommandLineText();

        GetCommandLineArguments(sCommandLineCaption, param);

        if (param.getParametersCount() > 0)
        {
            std::string sCommandName = param.getParameterAsString("0");

            // On appel la commande spécifiée
            CONSOLE_COMMANDS_MANAGER->InvokeCommand(sCommandName, param);

            AddToHistory( sCommandLineCaption );
        }
            
        m_pPrompt->Clear();
    }
    else if (arg.key == DIK_LEFTARROW)
    {
        m_pPrompt->MoveCursorToLeft();
    }
    else if (arg.key == DIK_RIGHTARROW)
    {
        m_pPrompt->MoveCursorToRight();
    }
    else if (arg.key == DIK_BACK)
    {
        DeletePreviousCharacter();
    }
    else if (arg.key == DIK_DELETE)
    {
        DeleteNextCharacter();
    }
    else if (arg.key == DIK_TAB)
    {
        TabComplete();
        m_pPrompt->ResetCursorPos();
    }
    else if(arg.key == DIK_UP)
    {
        const std::wstring sCommand = m_pHistory->GetPrevious();

        if (sCommand != EMPTY)
        {
            m_pPrompt->SetText(sCommand);
            m_pPrompt->ResetCursorPos();
        }
    }
    else if(arg.key == DIK_DOWN)
    {
        const std::wstring sCommand = m_pHistory->GetNext();

        if (sCommand != EMPTY)
        {
            m_pPrompt->SetText(sCommand);
            m_pPrompt->ResetCursorPos();
        }
    }
}

void Console::SoloKeyReleased(const KeyEvent& arg)
{
}    

void Console::RepetitiveKeyPressed(const KeyEvent& arg)
{
    if (!IsVisible())
    {
        return;
    }

    if (arg.key == (DIK_PGUP))
    {
        IncreaseScrollingPosition();
    }

    if (arg.key == (DIK_PGDN))
    {
        DescreaseScrollingPosition();
    }
}

void Console::RepetitiveKeyReleased(const KeyEvent& arg)
{
}    

void Console::GetCommandLineArguments(const std::wstring& sCommandLine, DataParameters& param)
{
    std::string sArgument = "";
    unsigned int iArgumentCount = 0;

    for (unsigned int c = 0; c < sCommandLine.length(); c++)
    {
        if (sCommandLine1 == ' ')
        {
            if (sArgument.length() > 0)
            {
                param.setParam(iArgumentCount, sArgument);

                iArgumentCount++;
                sArgument = "";
            }
        }
        else
        {
            sArgument += sCommandLine1;
        }
    }

    if (sArgument.length() > 0)
    {
        param.setParam(iArgumentCount, sArgument);
    }
}

void Console::Render(float fTimeElapsedSinceLastFrame)
{    
    if (!IsVisible())
    {
        return;
    }

    UpdateSprites(fTimeElapsedSinceLastFrame);

    // On commence par afficher le texte à partir d'une certaine marge
    float iLastTextElementXPos = CONSOLE_LINE_BEGINNING_WIDTH;
    int iLineID = 0;
    float fLastLineYPos = 0;
    float fScroll = -(m_fScrollOffset * m_fConsoleHeight);
    float fHeightPadding = m_fCharHeight / 2 + CONSOLE_HEIGHT_PADDING;

    m_pConsoleTextSprite->Begin(D3DX10_SPRITE_SAVE_STATE);
    for (unsigned int i = m_iLinesStart; i < m_lines.size() - m_iLinesOffset; i++)
    {
        Line* pLine = m_lines[i];

        std::vector<TextElement*> elems;
        pLine->GetTextElements(elems);

        for (unsigned int j = 0; j < elems.size(); j++)
        {
            TextElement* pElement = elems[j];

            float fLastLineYPos = (float) (iLineID) * fHeightPadding;

            RECT rectangle = {iLastTextElementXPos, fScroll + fLastLineYPos, 0, 0};

            D3DXCOLOR color = pElement->GetColour();

            std::wstring sText = pElement->GetText();

            m_pFont->DrawText(m_pConsoleTextSprite, sText.c_str(), -1, &rectangle, DT_NOCLIP, color);            
            
            iLastTextElementXPos += GetStringLength( pElement->GetText() ) ;
        }

        iLastTextElementXPos = CONSOLE_LINE_BEGINNING_WIDTH;
        iLineID++;
    }

    /******** Command Line Prompt ********/

    const std::wstring sPromptText = m_pPrompt->GetPromptText();
    const std::wstring sCommandLineText = m_pPrompt->GetCommandLineText();

    float y = fScroll + (float) (m_iMaxLinesDisplayed) * fHeightPadding;

    RECT promptRectangle = {CONSOLE_LINE_BEGINNING_WIDTH, y, 0, 0};
    RECT commandLineRectangle = {sPromptText.length() * m_fCharWidth, y, 0, 0};

    D3DXCOLOR promptColor(1.0f, 0.85f, 0.0f, 1.0f);
    D3DXCOLOR commandLineColor(0.0f, 0.3f, 1.0f, 1.0f);

    m_pFont->DrawText(m_pConsoleTextSprite, sPromptText.c_str(), -1, &promptRectangle, DT_NOCLIP, promptColor);        
    m_pFont->DrawText(m_pConsoleTextSprite, sCommandLineText.c_str(), -1, &commandLineRectangle, DT_NOCLIP, commandLineColor);

    float fBorderScroll = y + m_fCharHeight + CONSOLE_HEIGHT_PADDING / 2;
    m_pBorderImage->SetPosition(0, fBorderScroll);

    float fY = y + (m_fCharHeight / 2 - CONSOLE_HEIGHT_PADDING / 2);
    m_pPrompt->Update(fY);

    m_pConsoleTextSprite->End();

    // On affiche le prompt après tous les précédents rendus
    m_pPrompt->Render();
}

void Console::UpdateSprites(float fTimeElapsedSinceLastFrame)
{
    if (!IsVisible())
    {
        return;
    }

    m_pBackgroudImage->Render();
    m_pBorderImage->Render();

    // Montrer
    if (m_state == MOVING_DOWN)
    {      
        m_fScrollOffset -= fTimeElapsedSinceLastFrame * CONSOLE_MOVE_SPEED;

        if (m_fScrollOffset <= 0.0)
        {
            m_fScrollOffset = 0.0;

              SetState(SHOWN);
        } 

        m_pBackgroudImage->SetPosition(0, -m_fScrollOffset * m_fConsoleHeight);
    }
 
    // Cacher
    if (m_state == MOVING_UP)
    {
        m_fScrollOffset += fTimeElapsedSinceLastFrame * CONSOLE_MOVE_SPEED;

        if (m_fScrollOffset >= 1.0)
        {
            m_fScrollOffset = 1.0;

              SetState(HIDDEN);
        }

        m_pBackgroudImage->SetPosition(0, -m_fScrollOffset * m_fConsoleHeight);
    }

    // Mise à jour des éléments qui alterne en couleur alpha
    if (m_state == SHOWN)
    {
        static float alpha = 0.0f;

        if (m_elemState == TransparencyState::FADE_IN)
        {
            alpha -= fTimeElapsedSinceLastFrame;

            if (alpha < 0.0f)
            {
                m_elemState = TransparencyState::FADE_OUT;
            }
        }
        else if (m_elemState == TransparencyState::FADE_OUT)
        {
            alpha += fTimeElapsedSinceLastFrame;
    
            if (alpha > 1.0f)
            {
                m_elemState = TransparencyState::FADE_IN;
            }
        }

        for (unsigned int i = 0; i < m_blinkingTextElements.size(); i++)
        {        
            TextElement* pTextElem = m_blinkingTextElements[i];
            static D3DXCOLOR color = pTextElem->GetColour();

            pTextElem->SetColour(D3DXCOLOR(color.r, color.g, color.b, alpha));
        }

        m_pPrompt->SetArrowTransparency(alpha);
    }
}

/* todo : utils.cpp */
std::string Console::ToString(const std::wstring& sText)
{
    std::string text = "";

    text.assign(sText.begin(), sText.end());

    return text;
}

std::wstring Console::ToWString(const std::string& sText)
{
    std::wstring text = L"";

    text.assign(sText.begin(), sText.end());

    return text;
}

unsigned int Console::GetWidth()
{
    return m_fConsoleWidth;
}

bool Console::TextIsOutOfWidth(const std::wstring sText)
{
    // On applique un certain espace entre les bords de la console
    unsigned int iTotalWidth = GetStringLength(sText) + CONSOLE_LINE_BEGINNING_WIDTH * 4;

    return iTotalWidth >= GetWidth();
}

void Console::ResetScrollingPosition()
{
    if (m_lines.size() > m_iMaxLinesDisplayed)
    {
        for (int i = 0; i < m_lines.size(); i++)
        {
            if (m_iLinesOffset > 0)
            {
                m_iLinesOffset--;
                m_iLinesStart++;
            }
        }    
    }
}

unsigned int Console::GetCharactWidth()
{
    return m_fCharWidth;
}

void Console::DeleteNextCharacter()
{
    unsigned int iPromptCursorPos = m_pPrompt->GetCursorPos();

    std::wstring sCurrentCommandLine = m_pPrompt->GetCommandLineText();

    std::wstring sFirstPart = sCurrentCommandLine.substr(0, iPromptCursorPos);
    std::wstring sLastPart = sCurrentCommandLine.substr(iPromptCursorPos, sCurrentCommandLine.length());

    if (sFirstPart.length() >= 0)
    {
        if (sLastPart.length() > 0 )
        {    
            sLastPart.erase(0, 1);
            m_pPrompt->SetText(sFirstPart + sLastPart);    
        }
    }
}

void Console::DeletePreviousCharacter()
{        
    unsigned int iPromptCursorPos = m_pPrompt->GetCursorPos();

    std::wstring sCurrentCommandLine = m_pPrompt->GetCommandLineText();

    std::wstring sFirstPart = sCurrentCommandLine.substr(0, iPromptCursorPos);
    std::wstring sLastPart = sCurrentCommandLine.substr(iPromptCursorPos, sCurrentCommandLine.length());

    if (sFirstPart.length() > 0)
    {
        if (sLastPart.length() > 0 )
        {    
            sFirstPart.pop_back();
            m_pPrompt->SetText(sFirstPart + sLastPart);    
            m_pPrompt->MoveCursorToLeft();
        }
        else if (sCurrentCommandLine.length() > 0)
        {
            sCurrentCommandLine.pop_back();
            m_pPrompt->SetText(sCurrentCommandLine);
            m_pPrompt->MoveCursorToLeft();
        }
    }
}

Résumé :

Voici le code de cet article : archive.