hello there our objective in today's
video is to build a gui or a graphical
user interface
in rust but considering that we're
moving from solely developing command
line programs
a good way to transition is to get
started with two weeks text-based user
interfaces
as it introduces us to the event-driven
architecture that is common in gooeys
so
so as usual to avoid complex business
logic the app will be developing is
simple
but should give you the tools to get
started creating your own programs
which is hopefully why you're watching
this video
all right so anyway with that out of the
way we'll
be building a tui and a gui pikase
where pikasay is the command line
program we developed in the first video
project
of the series if you haven't watched
that yet i'll link it down below
anyway for the tui we'll build this
and upon startup the user will be
greeted with
one a text input where we can specify
what picture will say
and two a check box to indicate whether
pikachu
is in a pokeball or not just like in the
psa
uh command line program so once you fill
those in
say hello you can tab through the inputs
press ok
it displays this or you can check return
press ok and it shows
the pokeball and you can also exit
all right so this is the outcome you
should get now for the gui
doing cargo run shows
this um pretty intuitive ui uh
same logic as the tui you can say
something like
hello press generate and pikachu shows
up saying hello
or you can put pikachu in a pokeball
press generate and pikachu shows up in a
pokeball
alright so let's get started
all right so for our tui we'll be using
a crate called cursive
as it hides uh some annoying details and
it is overall pretty easy to use
um but before we get started it's
important to know the cursive makes use
of the system library and curses
which means you must have that installed
on your computer
so i'm currently using ubuntu so to get
it installed
all you had all i had to do was say sudo
apt-get install lib
and versus w5
dev okay
i already have this install so i won't
run it but
if you're developing for windows and or
mac os
i'll leave a link in the description for
the equivalent installations there are
several backends that you can use i
don't think and curses is available for
windows but you can install pancreases
and whatnot
so yeah find that in the description so
with that out of the way
um let's get started with this new
binary crate we'll add uh dependency to
cursive let's go to the cargo tommo
and i have it opened right here copy
this and we'll add it right there
all right so this done we'll go back to
our main
and to start the app loop is pretty
simple we just say
use cursive and here in main
we can't close this
we can just say let
mutable app equals cursive
default and then we say app.run
all right so here we are as i mentioned
just starting our
event loop um usually in command line
programs we're just limited to
one input at a time so we never really
have to worry about
event driven systems but for guise on
the other hand
we might have to handle multiple inputs
like i don't know multiple key presses
mouse clicks and whatnot
so since we can't really predict which
input will be triggered first the
program
runs a sort of a conceptual while loop
to monitor things and
if you don't really get this right now
don't worry it'll become
clear once we actually start handling
some events
anyway if we run our program
right now so just cargo run it of course
it will
compile a bunch of things but as
you'll see all we get is a
blue screen now usually that's not
uh very good but this time we can just
control see it
so to to close the program so
but once this finishes compiling
if takes too long i'll speed up the
video now
all right so as i mentioned good old
blue screen
uh but let me just control c
and it closes it just fine anyway
let's actually bring some life onto it
with
let's bring our pikachu onto scope
like so first order of business
will just display pikachu on a
textview so we'll say cursive views
and we'll import a text view
right now in here uh to actually display
it
we can just add a layer with our pikachu
and a text view onto the app
now um all sables will say
what we'll say is app add layer
and we'll say text view which is what we
just brought on scope
and new pikachu
alright now i mentioned layers and
views so what really are them
well views are the main building blocks
of the cursive library
an example of a view is the one we're
just using the text view
which just holds text you can also use
views for buttons
check boxes dialogues progress bars
and a lot more now layers are important
because
cursive uses them to create a stacked
display of views uh where the last one
where the last added view is the active
one
anyway basics out of the way let's run
our program
oops cargo run and
i think we have an error oops i didn't
save
my bad
and there we go there's our pikachu
um but this
textview isn't good enough so let's
other than ctrl c to terminate the
execution of the binary our program
isn't really
listening to anything let's remedy that
by adding the option to quit the program
by pressing the escape key
which community is pretty simple we can
just say app dot
add global callback
and in here our event will be
key escape and
our callback will be
s dot quit
of course we have to
put s in there and before i forget we
don't really have keys here
so let's just say use cursive
i think that's event event event yeah
and key
all right um so in here other than
the layer that we added previously we're
adding a global callback
what does that mean it means the
disclosure isn't being called
right now instead we're sort of
registering or adding a callback so that
whenever we have a specified event
happen which in this case is the escape
key being pressed
we can just call back to it
that's why it's a callback so
now you're hopefully starting to
understand a bit better why we have this
conceptual infinite loop
we could have several callbacks and this
loop is just waiting for any of them to
get
all called back anyway there isn't much
point to me running this
part because you can't really see my
keyboard but if you're following along
try pressing escape when the program is
running and you'll see it
terminate all right so
regardless of our progress our app still
feels a bit rough around the edges so
let's do something about that by
rather than describing what a dialogue
is
i'll just show you so i've added some
new code
i brought in dialogue and i put a
dialogue around
our textview pikachu now let's bring the
terminal
say cargo run
and there it is so now you get why it
was rough around the edges
anyway that's niche but the really cool
thing about this
is that we can press escape to close the
program
um the really cool thing that we can do
is that we can come
here and say dot
uh button but we'll say
okay and another closure we'll just say
s
um stock quit
and now if we run it if i go run
we see that we have the little button
which if we press
close the program and that's pretty cool
um anyway now that we have these basics
out of the way
let's add layers onto layers onto layers
on
all right so for this section we'll
write more than just a
couple of lines of code so usually what
i would do
is i would sort of just write the code
and then speed up the footage
but this time around i've written the
code in advance
and now i'll just copy and paste it
i'm not sure which method is best let me
know if one is better than the other
and i'll i'll just go with that but
anyway um i'll
paste it in for now and
now i'll just go go over it but before
getting started with
any logic um i want to point out
that i am bringing the pokeball
and some cursive stuff um into scope
so if you're following along do that
um but now as we mentioned previously
the first thing we want to do is create
a form page
where the user can input a message for
pikachu to say
that's what we're doing in this input
step
that we're calling in me going
over it
we see that we are
adding a layer with a dialog inside
which has a title some content
and a call back button now the title is
just
simple text the content
is a list view to display our
text input so the edit view
and our check box
in a scrollable list container layout
and our callback button
just populates this picsay
options struct that i created right here
good example of using lifetimes in
actual code
if you're looking for one but
yeah so going back
here we're just populating with um
message
and the pokeball return state
but how how are we actually doing that
i'll get into it in more detail
um to achieve that work we're giving
each usable user interactable view
a name with this with name
function which by the way is
conveniently brought into scope
with our first use on this identifiable
and now having a name we
can get its
content or simply check if it is checked
with this column name so here we're
getting content
and here we're checking if it's checked
um
i mean of course maybe we misspelled the
name or we try to get something that
doesn't really exist in
in the window so this call name returns
an option
but that's all right nothing that
unwrapping can solve
and having all this we're ready to move
on to the next step
where just like in the previous pic
essay we determined that the text
would determine the text will be
displayed in accordance to the user
input
and that's
done right here
and having done that we pop
our previous layer now pretty stack like
fashion so we can add this new
result layer with a
usual title and a button
just to quit alright
so hopefully i went
over that all right usually it's easier
to do with the speed coding but i don't
know let me know let me know how it was
and yeah this result step is being
called in here so after the button
right so call result step go to the next
layer pop the layer add the new layer
let's get to the fun stuff and actually
run this now doing cargo run
um shouldn't take too long there it is
we
can now say something like hello
press ok it shows up
and now it closes we can then cargo run
it again
check this press ok now we get a
pokeball
all right um you'll note that in the one
that i showed in the beginning
instead you could press ok and instead
of quitting it would go
back to the previous layer and you also
had an exit button on the previous layer
so if you want to try that out
as sort of a additional exercise go
ahead
but i think this was a long introduction
and to create our graphical user
interface we'll be using gtk
uh where gtk is this uh free open source
and well established library for
building guise
just like cursive jtk also relies on the
system library
to install it on ubuntu you can simply
do
sudo apt-get install
lib jtk
dev right again for other operating
systems
i'll leave it in the instructions below
or in the description i'll leave the
instructions in the description below
all right being done with this um we can
go to our
newly created binary crate
and add dependency to the rust jtk
binding
it's a bit more complicated than usual
but not very much all you have to do
that's why i'll write it specifically
instead of copying from
creator you'll say gio and explain what
joe is in a second
and then you'll say depends
dependencies.gtk
version the latest version is version
0.9.2 as far as i know
and features which will say the
underscore or sorry v3 underscore
super all right let's save this
and first we have um
gio which is this guy
um and it is needed for a gtk
as you'll see in a second and then we
have as i spoiled gtk
itself um to find the system
gtk version um that we're using in
features
all you have to do is say the package
l and then you can look it up by gtk3
oh as you'll see right here
i'm using three point two four and
that's why i have three
v three underscore two four
all right um
and being done with this
um we can get started with some skeleton
code
let's come in here and let's paste this
in
now i can close all this for now
and save it alright so let's go
over it you'll see that the general flow
is actually pretty similar to the tui
which is why that was the introduction
um anyway we have the conceptual
infinite loop
with app.run but before we get into that
let's go
to the beginning of main so here
we set our fid
which uses um this is jtk style uses
sort of
reverse um dns so you say com
then logs because i gui which is the
name of
the specific binary crate but you can
call it whatever you want
following convention of course now flag
is this
second part there are they are what
defines sort of the
behavior of an application um there are
several examples
and i have that open in here
so as you can see we're using flags none
which is the the default but
you have several you have a service as
launcher
and we'll just use this first one but if
you're
feeling adventurous you can go over it
again i'll leave this in the description
anyway yeah for now we'll use no flags
which is the default
um but moving on after this
we set a callback
for when the app is active
and in here we create a
window we set a title we
set its default size and then we show it
as otherwise it will just remain hidden
it will be there but it won't show up
and then lastly as we
mentioned previously we have here our
conceptual
infinite loop the one difference is that
as you can see we're passing all command
line arguments
all right let's actually run this now if
we go to here and we do a cargo
run hopefully won't take too long but
if it does um i'll speed it up
and i think i will speed it up
and there we go all that wait
for a very exciting empty window
but um
anyway let's add something to it
all right for this section we'll need a
couple of
new things from jtk other than the
application and the application window
let me
minimize this we'll also need
a box a label
[Music]
an image and orientation
all right now with this there
um all we have to do is
this is this magic of copy and paste
um again as usual um usually i'll speed
up this footage
but let me know if that's better or
worse
anyway um so i'm boarding a couple new
things
and then in terms of logic here we're
creating a
layout box which is used to organize
our widgets we're specifying that we
want
a vertical orientation so our widgets
will be placed one
on top of the other and
with a spacing of zero in between them
okay
after that we create um
we create our message in a label and
we add that to the layout so you can
sort of see how the pattern is
we create an image and we add that onto
the layout
and then finally we get our layout
and we add it to the window
this is what we just had before so
windows showa
and hopefully this will be a more
entertaining
display just come here we'll say cargo
run and we haven't saved it
so wasn't that all that much
entertaining
we'll do cargo run and
i just noticed i forgot to put pikachu
and the folder so i'll be right back
all right so a couple of changes
um as i mentioned i had i didn't i
didn't have pikachu
in the image for pikachu anywhere
or in the specified path so i brought
that in images
same level of source we have pikachu.png
and i noticed that the image that i have
was a png
in here was a jpeg so i just changed
that to be png
but in any case now we can do cargo
run and there is our good old
surprise pikachu cool
but before we move on
i just have one quick note you see that
we are defining
layout from our rust code
and i mean our example is pretty simple
so it's sort of easy to see if any
problems arose and why they are arising
but what if you had a huge layout
right um well you're in luck
because jtk provides us with a debug
view
for widgets and to make use of it
we just do just use the jtk
debug environment variable and we set it
to interactive
then we do cargo run so doing this
we'll see that our program runs
but then it also opened this one though
and
this is pretty cool because it can help
you visualize um
for example if we open our window you
see that
sort of flickered then we have our
layout box
our label and our image so
again not super helpful with our very
simple example but
yeah feel free to make use of it with
your huge layouts there are to come
all right talking about huge layouts
let's move on to
some maybe not huge but definitely more
complex
layouts
so building ui procedurally like we just
did it's very hard as your program goes
larger
so to solve that i want to introduce you
to glade
the ui design tool that comes with gtk
which will allow us to build our ui
declaratively alright so to make use of
glade
and ubuntu all i had to do was say sudo
apt-get
install glade if you are on windows or
osx
i'll leave the link for the installation
down below
all right i've already installed this
but having this installed i can now go
to my terminal and i can say
glade which uh
some stuff will run but it will open an
app a window like
so now by creating a new file
or a new project clicking this icon will
give me a window like this
which pretty much gives me a tool a drag
and drop tool
for creating your layout style um
all right to sort of introduce you to
this i'll let's work at the first part
together
and then i'll speed up the rest so you
see the end result
first we want to add our application
window so
let's go to top levels and look up
application window double click it
adds it onto the cam onto our canvas
all right looking good now let's add um
or before we do that let's actually give
this an id we'll call it application
window
one and now let's add our
sort of our global layout box
um with three items inside so we'll be
at jtk box with drag
and drop option now
just like in our code we can see that we
can set the orientation to be
vertical and spacing of zero so
pretty much just the code but
yeah let's call this what i mentioned
global
layout box
and now moving on let's create our form
box
so i'll create a new container and i'll
add it
in there this will be our form
layout box and
it will also have three items and an
orientation
vertical we'll have three items because
one is the message box
one is the pokeball box where you say if
it's returned
and the last one is a button to generate
our figure
all right let's start by creating the
message box
so box you add it onto
there and we'll say
message input
box but now this one will be
horizontal we'll have two items and
we'll be homogeneous
what this means is that both items that
we add on the box will have the same
size so this is just so that it looks
nice
okay now
let's get our first display item which
will be a gtk label
we'll add it on to there and we'll say
something is very useful pikapeak
um and this will be our message
input label now in control
we want to add a jtk entry for where the
user will enter their message
in here we can just call this message
input
we can pretty much leave everything as
default
it doesn't really matter but if you want
to play around with it go
right ahead um
okay and i think this
sort of gives you the gist of it um i'll
now speed up the rest
and i'll see you in a bit
[Music]
all right and once you've finished
um hopefully it will look somewhat like
this
but if not put your own uh twist to it
and i don't know have fun but now that
we're done
we can just um save
and we'll save this in our
root of um
the project which by the way i separated
i created a new binary crate
called pikasay gui glade and
i pretty much just copied everything
that we had in the other one
onto this one just so that they're
separated but they're the same
all right yeah so we'll save um
we'll call this layout and we'll use the
dot
glade extension okay
but now that we have
this how do we make
use of it in the code so
let's go back to the code
right in here maximize this all right so
our objective here is to
really just remove all this we'll get
rid of it and instead
super fast coding there it is
instead we'll include our
layout as a string and we'll build it
we'll build from the string okay so
we'll be building directly from our
layout
which by the way if you want to see the
file
it's really just xml so
pretty cool uh but going back to main
so what's happening here um have it as a
string
built from a string we then
got our window now i think we called
it let's go back in here
we called the application 1.1 so
that's oops raise it that's what we're
getting the object from so we still we
use the names that we
had on glade to refer to them in code
and then we tie the the window that we
just got
from the builder to our app and we show
up
so let's run this um i'll close
i'll close glade and
i'll cargo run
now this will take a little bit so
i'll speed it up and be right back
[Music]
all right and with that there we go
um and it's
looking great but it doesn't really have
any functionality if i
say whatever i press generate nothing
really happens
if i turn on the switch press generate
nothing really happens so let's remedy
that by
and the way we'll do this is actually
pretty similar to how it did in our tui
um inside our connect activate
let me paste this in and now as usual
let's
walk through it uh oh actually one more
thing
below here we want to say image output
dot hide
okay um our avs code is already spoiling
that we have a problem here but we'll
get
to that in a second before we get there
you see that just how we got our window
we can get
the other objects such as our message
input
or button or message output and our
image output okay so
yeah and then we can then use these
to assign for example a specific
callback
such as for when the button is clicked
now let's just go over the logic and
then we'll see the problem in the logic
in here we'll have our button clicked
and we want to happen
is that we want to get the message input
right so whatever we get in the entry
and then display
that or update or message output label
with that and
yeah but one thing that's important is
that we don't want to show the pikachu
just being surprised without saying
anything so
after we show all the layers
we'll be hiding the image output so
that's why we're showing the image here
i'm sort of
jumping around but hopefully that makes
sense right this this is a callback
so it only gets called um when the event
is triggered
so in this case the button is clicked um
so for for now this is this callback is
just registered we show all
but we hide the image output and the
order is important
okay so now back to what we were
discussing before the callback
we we get the message input
that the the user entered um
this has a type of g string so
what i'm doing is i'm checking if it's
empty if it's empty we just want to
display the image created with analogs
else we want to set the message as
that as a string okay then
we set the text in the message output
with the formatted bubble and
inside our bubble our message okay then
we'll show the image output because
again
it will the pikachu will be hit
but again as we're being spoiled
closure may outlive the current function
but it borrows image output which is
owned by
the current function may outlive
borrowed valued
image output all right so
this is happening because once the
callback function is set
it might get triggered anytime during
the application's lifetime
but by the time the callback is
triggered
image output variable will already have
gone out of scope
and to fix this you may think oh okay
let's just
uh move ownership of the image output
right so we'll say
move now we're moving the ownership of
that into the scope and so we don't have
an issue
but then another problem occurs because
we're making use of it here we're hiding
it after it
so just display it borrowed
of mood value image output
and a simple fix to this is
to clone it and before you complain
just just let me explain or if you're
not complaining then
just just be happy with it but i'll
explain why cloning in this case isn't
bad people usually complain about
cloning but
you'll see in just a sec we'll call this
an image output clone
and we'll say imageoutput.clone right
now we'll be using instead the clone
in here and we'll be hiding the normal
image output
and now we can move and we don't really
have any errors
alright so again
we are oh just before in case you're
curious we're still moving
message output and message input into
the closure
but we're just we're not cloning them
because we're not using them like we do
image output
but anyway back to cloning this clone
we're making is actually pretty low cost
because considering that gdkrs
is just a wrapper around gtk from c
it makes it so that we're only copying
the pointer and not the whole data
structure which is
usually why people complain
yeah the only thing left to do is
to put a pikachu in a pokeball
um but for now let's see let's see how
this is looking let's save it
and go to our terminal
oh by the way we saw that we had a
couple of unused um
uses so i just uh removed them
but cargo running this
hopefully won't take too long uh but if
it does i'll just speed it up
as usual shouldn't though
right just just one and there we go okay
all right so as we can see our pikachu
is not being displayed because
we hid the image output and now we can
say
hello press generate there we go
we got our pikachu saying hello all
right let's move on
to putting our pikachu in a pokeball by
which nicely enough is pretty easy to do
so we're reaching the end of the video
so feeling daring
pause the video right now and try to do
it yourself
but with that said here's how to do it
select it all because that's pretty much
where the change is happening with
exception of this
so we're just as usual bringing
we're getting that object from the
builder and
now in our callback we are checking if
the return switch is active
and keeping that in this is return
variable
um now
we check if it is returned if it is
returned
we set the output text to an empty
string
because we don't want to have pikachu
same things from inside the pokeball
and we set the image from file
now one thing that i want you to note is
the path so when i was
creating the layout for in glade
i added this to this images folder to
our project
and note that it is in the same level as
source
but we're doing dot slash images
pokeball.png
okay so
it's the path from
the layout.glade file that's something
important to note
but anyway we'll set it as a pokeball
else
so if pikachu's has not returned we will
set its pikachu and format the message
in the normal way and
that is it um
let's go to our terminal let's cargo run
it
and with that we should be done
um i don't think this will take too long
but yeah it took me forever to make this
video so
hopefully the wait was worth it
anyway um all right here pika pika we'll
say
hello generate it shows up perfect
now if i say returned generate
we see the pokeball yay
um and then again i can change
the message if i generate and that's
active it won't show
but if i remove it generate then choose
so
yeah there it is um again to prepare for
this video took me forever
if you're in the discord you know uh but
again hopefully it was worth it to wait
and thank you so much for watching i
guess i'll see you next time
[Music]
you