Skip to content

Instantly share code, notes, and snippets.

@malustewart
Last active April 1, 2020 00:35
Show Gist options
  • Save malustewart/e1609d2359a677e60d221a91c5377d7c to your computer and use it in GitHub Desktop.
Save malustewart/e1609d2359a677e60d221a91c5377d7c to your computer and use it in GitHub Desktop.
DearImGui guia para primeros pasos
\documentclass[12pt]{article}
\usepackage{graphicx}
\usepackage{hyperref}
\hypersetup{
colorlinks=true,
linkcolor=blue,
filecolor=magenta,
urlcolor=cyan,
}
\usepackage{listings}
\usepackage{xcolor}
\usepackage{float}
\definecolor{codegreen}{rgb}{0,0.6,0}
\definecolor{codegray}{rgb}{0.5,0.5,0.5}
\definecolor{codepurple}{rgb}{0.58,0,0.82}
\definecolor{backcolour}{rgb}{1,1,1}
\lstdefinestyle{mystyle}{
backgroundcolor=\color{backcolour},
commentstyle=\color{codegreen},
keywordstyle=\color{magenta},
numberstyle=\tiny\color{codegray},
stringstyle=\color{codepurple},
basicstyle=\ttfamily\footnotesize,
breakatwhitespace=false,
breaklines=true,
captionpos=b,
keepspaces=true,
numbers=left,
numbersep=5pt,
showspaces=false,
showstringspaces=false,
showtabs=false,
tabsize=2
}
\lstset{style=mystyle, language=c}
\newcommand\Warning{%
\makebox[1.4em][c]{%
\makebox[0pt][c]{\raisebox{.1em}{\small!}}%
\makebox[0pt][c]{\color{red}\Large$\bigtriangleup$}}}%
\pagestyle{myheadings}
\markright{Malu Stewart Harris\hfill Gu\'ia introductoria a Dear ImGui\hfill}
\begin{document}
\title{Gu\'ia introductoria a Dear ImGui}
\author{Malu Stewart Harris}
\date{EDA --- Primer cuatrimestre 2020}
\maketitle
\section{Instalaci\'on}
\begin{itemize}
\item Instalar Allegro con Nuget.
\item Clonar el repositorio de Dear ImGui desde \url{https://github.com/ocornut/imgui}
\item Ir al directorio principal del repositorio y copiar e incluir al proyecto los siguientes archivos:
\begin{itemize}
\item \texttt{imconfig.h}
\item \texttt{imgui.cpp}
\item \texttt{imgui.h}
\item \texttt{imgui\_demo.cpp}
\item \texttt{imgui\_draw.cpp}
\item \texttt{imgui\_internal.h}
\item \texttt{imgui\_widgets.cpp}
\item \texttt{imstb\_rectpack.h}
\item \texttt{imstb\_textedit.h}
\item \texttt{imstb\_truetype.h}
\end{itemize}
\item Ir a la carpeta \texttt{examples} y copiar e incluir al proyecto los siguientes archivos:
\begin{itemize}
\item \texttt{imgui\_impl\_allegro5.cpp}
\item \texttt{imgui\_impl\_allegro5.h}
\end{itemize}
\end{itemize}
Ya se encuentra todo configurado para usar Dear ImGui. Para ver un ejemplo, usar el archivo \texttt{main.c} ubicado en \texttt{examples/example\_allegro5}.
\section{Uso}
Todos los ejemplos se encuentran en el archivo
\href{https://gist.github.com/ebe0f9fdb0ef9db016a2ccb7422228a5}{main.cpp} compartido. Para correrlos, definir una macro con el nombre del ejemplo, el cual se encuentra en la primera l\'inea de su c\'odigo.\par Tambi\'en se incluye el archivo \href{https://gist.github.com/e23db37d90e66f42f1ea1c2461fd04dd}{imgui\_main\_loop\_pseudocode.c} que contiene un esquema de los bloques b\'asicos necesarios en un programa que utiliza Dear ImGui.
\subsection{Widgets}
ImGui cuenta con una gran variedad de widgets. Existen simples (por ejemplo \texttt{ImGui::Text}, que solamente escribe texto, o \texttt{ImGui::Checkbox}, que genera una casilla que se puede tildar y destildar) y complejos (por ejemplo \texttt{ImGui::PlotHistogram}, que dibuja histogramas). Se pueden ver todos en la ventana \texttt{demo\_window}, disponible en el c\'odigo de ejemplo descargado desde el repositorio.
\begin{figure}[H]
\centering
\includegraphics[scale=1]{ej_histogram.png}
\end{figure}
\begin{figure}[H]
\centering
\includegraphics[scale=1]{ej_checkbox.png}
\end{figure}
Usualmente deben poder recibir o devolver informaci\'on. Por ejemplo, \texttt{ImGui::PlotHistogram} recibe la data a plotear, y \texttt{ImGui::Button} devuelve si fue apretado o no (ver secci\'on \ref{ssec:process_vent} sobre interpretaci\'on de datos de usuario).
\subsubsection{\texttt{ImGui::Button}}
Para dibujar un bot\'on, alcanza con llamar a la funci\'on \texttt{ImGui::Button}, la cual recibe como par\'ametro el \textit{label} que se muestra al usuario. Esta devuelve \texttt{true} si en ese frame fue apretado, y \texttt{false} en caso contrario.
\begin{figure}[H]
\centering
\begin{minipage}{.7\textwidth}
\centering
\begin{lstlisting}
//EJ_BUTTON
ImGui::Begin("Button Example");
if (ImGui::Button("Increase intensity"))
intensity++;
else if (ImGui::Button("Decrease Intensity"))
intensity--;
ImGui::Text("Intensity = %d", intensity);
ImGui::End();\end{lstlisting}
\end{minipage}%
\begin{minipage}{.3\textwidth}
\centering
\includegraphics[scale=1]{ej_button.png}
\end{minipage}
\end{figure}
\fbox{
\begin{minipage}{0.8\textwidth}
\Warning Al igual que para todos los widgets, la funci\'on que lo crea debe llamarse en todos los frames en los que aparece.
DearImGui no tiene memoria de los widgets que se dibujaron en los frames anteriores.
Si en un frame no se llama, no existe.
\end{minipage}
}
\subsubsection{\texttt{ImGui::Checkbox}}
El checkbox funciona levemente diferente que el bot\'on, ya que debe poder recibir informaci\'on adem\'as de devolver.
Al crearlo es necesario indicar si se encuentra seleccionado o deseleccionado, y a su vez, debe avisar si el usuario cambi\'o su estado.
Para lograr esto, la funci\'on \texttt{ImGui::Checkbox} recibe, adem\'as de su \textit{label}, un puntero a una variable del tipo \texttt{bool}.
Si al llamar la funci\'on el \texttt{bool} es \texttt{true}, el checkbox se encuentra seleccionado, y si es \texttt{false}, se .
Si la funci\'on detecta que fue apretado, cambia su valor a \texttt{false}.
De esta forma, despu\'es de la funci\'on alcanza con preguntar el valor del \texttt{bool} enviado para saber si se encuentra seleccionado o deseleccionado.
\begin{figure}[H]
\centering
\begin{minipage}{.7\textwidth}
\centering
\begin{lstlisting}
//EJ_CHECKBOX
ImGui::Begin("Checkbox Example");
ImGui::Checkbox("Checkbox A", &checkbox_A_selected);
if (checkbox_A_selected)
ImGui::Text("Checkbox A seleccionado");
ImGui::End();\end{lstlisting}
\end{minipage}%
\begin{minipage}{.3\textwidth}
\centering
\includegraphics[scale=1]{ej_checkbox_bis.png}
\end{minipage}
\end{figure}
\fbox{
\begin{minipage}{0.8\textwidth}
\Warning La definici\'on del \texttt{bool} que se env\'ia a \texttt{ImGui::Checkbox} debe estar fuera del loop de dibujo.
De lo contrario, a la funci\'on siempre le va a llegar con \texttt{bool} con el mismo valor, y no con el valor modificado en el frame anterior en caso de que haya sido apretado por el usuario.
\end{minipage}
}
\subsection{\texttt{ImGui::Begin("Window name")} y \texttt{ImGui::End()}}
Estas funciones encierran widgets que pertenecen a una misma ventana. El nombre visible por el usuario de la ventana, o \textit{label}, es aquel que se le pase como par\'ametro a \texttt{ImGui::Begin}.
\begin{itemize}
\item Para crear m\'as de una ventana, se pueden usar pares \texttt{Begin - End} consecutivos:
\begin{figure}[H]
\centering
\begin{minipage}{.7\textwidth}
\centering
\begin{lstlisting}
//EJ_BEGIN_END_TWO_WINDOWS
ImGui::Begin("Primera Ventana");
ImGui::Text("Primer Texto");
ImGui::End();
ImGui::Begin("Segunda Ventana");
ImGui::Text("Segundo Texto");
ImGui::End();\end{lstlisting}
\end{minipage}%
\begin{minipage}{.3\textwidth}
\centering
\includegraphics[scale=1.4]{ej_begin_end_two_windows.PNG}
\end{minipage}
\end{figure}
\item El \textit{label} es tambi\'en usado como \textit{ID} interno de la ventana, el cual puede referenciarla dentro del c\'odigo. Esto permite asignar widgets a la misma ventana en distintos pares \texttt{Begin-End}:
\begin{figure}[H]
\centering
\begin{minipage}{.7\textwidth}
\centering
\begin{lstlisting}
//EJ_BEGIN_END_SAME_ID
ImGui::Begin("Label 1");
ImGui::Text("Primer texto");
ImGui::End();
ImGui::Begin("Label 2");
ImGui::Text("Segundo texto");
ImGui::End();
ImGui::Begin("Label 1");
ImGui::Text("Tercer texto"); //Se va a escribir en la primer ventana,
ImGui::End();
\end{lstlisting}
\end{minipage}%
\begin{minipage}{.3\textwidth}
\centering
\includegraphics[scale=1.4]{ej_begin_end_same_ID.PNG}
\end{minipage}
\end{figure}
\item Mirando el ejemplo anterior, se observa que ser\'ia imposible tener dos ventanas con el mismo \textit{label}, ya que tendr\'ian el mismo \textit{ID} y por lo tanto todos sus widgets se juntar\'ian en una sola. Para solucionar esto, existe una manera de asignar \textit{IDs} diferentes a ventanas con la misma \textit{label}:
\begin{figure}[H]
\centering
\begin{minipage}{.7\textwidth}
\centering
\begin{lstlisting}
//EJ_BEGIN_END_SAME_LABEL
ImGui::Begin("Label A##1"); // El ID de esta ventana es "Label A##1"
ImGui::Text("Primer texto");
ImGui::End();
ImGui::Begin("Label A##2"); // El ID de esta ventana es "Label A##2"
ImGui::Text("Segundo texto");
ImGui::End();\end{lstlisting}
\end{minipage}%
\begin{minipage}{.3\textwidth}
\centering
\includegraphics[scale=1.4]{ej_begin_end_same_label.PNG}
\end{minipage}
\end{figure}
\item Puede ponerse un \texttt{Begin-End} dentro de otro (similar al funcionamiento de un stack):
\begin{figure}[H]
\centering
\begin{minipage}{.7\textwidth}
\centering
\begin{lstlisting}
//EJ_BEGIN_END_STACK
ImGui::Begin("Primera ventana");
ImGui::Text("Primer texto");
ImGui::Begin("Segunda ventana");
ImGui::Text("Segundo texto");
ImGui::End();
ImGui::Text("Tercer texto"); //Se va a escribir en la primer ventana
ImGui::End();\end{lstlisting}
\end{minipage}%
\begin{minipage}{.3\textwidth}
\centering
\includegraphics[scale=1.4]{ej_begin_end_stack.PNG}
\end{minipage}
\end{figure}
\item \texttt{Begin} puede recibir opcionalmente un puntero a un bool, el cual pasa de valer \texttt{true} a \texttt{false} el momento en el que el usuario cerr\'o la ventana (nota: si no se pasa este puntero, la ventana no tiene la "X" para ser cerrada). Esto permite al programa facilmente saber cuando dejar de dibujar la ventana:
\begin{figure}[H]
\centering
\begin{minipage}{.5\textwidth}
\centering
\begin{lstlisting}
//EJ_BEGIN_END_CLOSING_BOOL
if (keep_open)
{
ImGui::Begin("Ventana que se cierra", &keep_open);
ImGui::Text("Tiene una 'X' porque puede cerrarse");
ImGui::End();
}\end{lstlisting}
\end{minipage}%
\begin{minipage}{.5\textwidth}
\centering
\includegraphics[scale=1.4]{ej_begin_end_closing_bool.PNG}
\end{minipage}
\end{figure}
\end{itemize}
\subsection{\texttt{ImGui::Render()} y \texttt{ImGui\_ImplAllegro5\_RenderDrawData(...)}}
Ambas funciones son necesarias para poder dibujar las ventanas. Se debe llamar \texttt{ImGui::Render()} para que DearImGui calcule todas las primitivas que deben ser dibujadas, y \texttt{ImGui\_ImplAllegro5\_RenderDrawData(ImGui::GetDrawData())} para que las dibuje. \texttt{ImGui::Render()} no utiliza Allegro, mientras que \texttt{ImGui\_ImplAllegro5\_RenderDrawData(ImGui::GetDrawData())} utiliza el add-on de primitivas de Allegro para dibujar. Ninguna de las dos funciones llama a \texttt{al\_flip\_display()}.
\subsection{\texttt{ImGui\_ImplAllegro5\_NewFrame()} y \texttt{ImGui::NewFrame()}}
Se deben llamar para iniciar el dibujo de cada pantalla.
\subsection{\texttt{ImGui\_ImplAllegro5\_ProcessEvent(\&ev)}}\label{ssec:process_vent}
Analiza si el evento recibido tiene influencia sobre la GUI de Dear ImGui y responde acordemente. Por ejemplo, si recibe un click, chequea si \'este indica el cierre de una ventana, y si es el caso, pone el \texttt{false} el bool correspondiente.
\fbox{
\begin{minipage}{0.8\textwidth}
\Warning Este proceso tiene que repetirse continuamente para que la GUI responda correctamente a las acciones del usuario. Por este motivo, no es conveniente utilizar funciones bloqueantes siempre que la GUI este en uso.
\end{minipage}
}
\end{document}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment