 Alright, let's go here, looks like I got microphone sound into OBS, I do not have desktop sound although there's nothing playing right now, a little bit out of center, let's edge that a bit, okay, I think we are about ready, let's see, with a lot of videos high, how's it going on YouTube there, happy Friday, yes, definitely echo that sentiment, have happy Friday, happy to, happy for Friday definitely, and I had the day off today, so, doubly happy Friday I guess, what, Gudabig, probably not pronouncing that one right, but how's it going to Dushipu and Tammy, over on YouTube, Nicholas, Paul, and, oh you told me this last time, is it, say, Biata, tell me it was like Miata the car, I remember if that's what you said or not, I apologize, I think maybe that might have been it, Biata if so, if I did get it wrong again, I apologize, but good day to you nonetheless, okay, so let's do a quick introduction, Randall in the YouTube chat, how's it going, let's, I have the chats on the screen right, I tweaked the chats to be partially transparent, I figured that might be nice so you can see the stuff in the bottom right corner of my screen, which I kind of realized before I was blocking, I don't know if it's going to be too hard to read if it is, we'll turn the transparency off or turn it down at least, so let me know if that is too hard to read the chats over there, especially as I start changing out different stuff on the screen, yes, okay, sweet, I did get the, I did remember the pronunciation guide correctly so that's good, CF Johnson, hi, let me also move OBS and so I can see the Discord chat as well, there we go, alright, so quick introduction, this is not circuitpython.org, this is circuitpython.org, so hello to everyone who is watching, my name is Tim and I go by FOMIGuy on Discord and GitHub as well as YouTube and Twitch and some other places, in this case we are streaming on the Adafruit Discord, so that's, you know, it's all under the Adafruit account, Adafruit YouTube and Adafruit Twitch as well, I think it also goes to some other platforms, I'm not sure of all of the different ones, but I think we're streaming in a couple other places as well, Mark Gambler, yay for Friday, it rained here this morning, but it's cleared out quite a bit actually since then, so again my name is Tim and I will be working on circuitpython related stuff, this is the deep dive program originally started by Scott, the lead developer of circuitpython, generally he was working on circuitpython itself, like making changes inside the core, adding new ports, adding new capabilities for the ports, that's kind of the stuff that was generally worked on on this show when he was on it, he's away right now for parental leave, I don't know exactly how much longer he's away, but I think we still have a couple of weeks at least, something like that, and so I'm filling in the deep dives, I am working on some similar kinds of things and some related kinds of things, I will say I tend to spend more time in Python land than I do sea land inside the core, although we have jumped into the core a couple of times on the stream as well and I'm sure we will again at some point also, but if we take just a moment to rewind, if you are new to all of this, if you haven't watched the stream before and none of what I just said makes any sense to you, don't worry, the high level look at what we're doing, you can find out more at circuitpython.org to learn more about the project that we are working with today, but the high level look at it is basically we're writing Python code that runs on these tiny computers called microcontrollers, there's lots of different shapes and sizes of these tiny computers, you can see some of them have screens, some of them are circular, some of them have keyboard buttons, some of them have Wi-Fi connectivity, lots of them have pins where you can hook up other peripherals, this one has an e-ink screen and a couple of buttons, so you can see these things come in all different shapes and sizes and the thing that they have in common is that they all run circuitpython, which is an implementation of Python that's made for these tiny computers, it runs on here, it makes them basically show up as a thumb drive, so if I kind of look in my computer here we have this circuitpy, which is actually a drive, this shows up just like if I were to plug in a thumb drive or an SD card or any other kind of mass storage, so it shows up right like this and on this there will be a file here called code.py, inside this file we are writing our Python code and as soon as we save it circuitpython will read the latest version and execute it for us and we can write code that interacts with all of the built-in peripherals, so you know if we're using one with a built-in e-ink screen or a built-in touch screen then we can draw stuff on the screen, if we're using one with a bunch of pins we can hook up to external sensors and beepers and buzzers and sensors and lights and pretty much any kind of little you know electronic doodad that you could imagine and so we're writing Python code that can interact with all that stuff. Again if you want to learn more circuitpython.org this is a good place to start, this is kind of the main website for the project. Also recommend folks who do want to learn more join us on the Discord that's the chat that is right underneath me I think right yeah right underneath me there in the in the stream the top one is the Discord chat and we are in the live broadcast chat room on the Adafruit Discord which the URL is shown down below at the bottom of the stream down there as well so adafru.it slash Discord you can head there and the team who works on circuitpython all collaborates inside of that Discord so you can get a good sense of like what's going on in the development of circuitpython if you want to join the Discord. Circuitpython is an open source project anybody is allowed to to work on it it's all hosted on GitHub all under open source licenses the company Adafruit this is their website adafruit.com this is the company that provides the primary financial backing for circuitpython though they're paying the team that works on the project full-time they're paying some folks like me who work on the project part-time and so they are the ones right now at least who are kind of paying the bills to keep circuitpython moving and growing and continually being developed and stuff so if you do want to support the project but not necessarily get involved in development and stuff like that what you can do is purchase your hardware from Adafruit and since they're paying the folks who are working on it you know if you support them by buying hardware that is helping support the circuitpython project as well so head to their website adafruit.com if you want to see the kinds of stuff that they have you know essentially they've got a bunch of the little microcontrollers and then they've got a bunch of things that you can kind of hook up to the microcontrollers to do other stuff with so lots of great stuff there let's me rearrange the windows just a little bit here air air conditioning repair guy nice got some AC which is good for the hundred degree weather he owes out earlier I went to the coffee shop that's down the street from my house it's kind of a a what's the word that's kind of like a habit that I have every every Friday my wife and I will go to a coffee shop down the street we both work from home so there's coffee shop just a couple of minutes away and on Fridays we'll go over there when we were there today we saw one of the other buildings in the same little shopping center they I think we're getting a new AC or something on the roof so there was like a crane truck that had lifted something up and put it on the roof it's neither here nor there but it was a crazy it was crazy thing to see because the truck they they lifted the front tires off the ground they had like posts that came out kind of lifted the whole front end of the truck like three feet off the ground or something to make it level or something like that so I don't know exactly why they did it but it was fascinating to see that that massive truck lift itself up off the ground like that that's my AC story for the day see Grover has gone okay before I jump straight into things today one thing that I will point out I we're not gonna do too much about it or with it in this stream but I just wanted to mention it is that Picon Picon US is taking place today was I think the first official day I there was some other stuff that was going on yesterday and I guess maybe the day before as well if you're in person there in Salt Lake City but today was the first day where they started doing the online stuff so I registered to attend Picon online I'm not in Salt Lake City I'm in my home which is in different city in the US and so I'm not there with all the rest of the folks there are some of the folks on the circuit Python team who are there in person so if you are watching and you happen to be in Salt Lake City and you want to meet some of the folks who work on circuit Python you can look for them while you're there I think Katnie and Jeff and Melissa maybe I think that's the three folks who went so you can meet those folks there I've been watching along with the videos most of the day and I'll probably go back to watching a couple more of them after the stream and yeah so if you are interested in Python more generally I definitely have enjoyed the talks that I've watched so far so I would encourage you if you're if you're interested in seeing them live and interacting with the community I assume maybe you can still register I'm not actually a hundred percent sure I registered a couple of days back so hopefully you can still register if not though I think they do share the videos online after the fact like you can go watch Picon videos from prior years on YouTube so my assumption is they'll eventually be uploading those things to YouTube and I do encourage folks to like check them out when they're when they're out there because there are been lots of interesting talks so far uh welcome to the beehive state to the beehive state yeah uh Utah is that the beehive state Utah is where Salt Lake City is where they're at physically for Picon so jumping into what I'm working on today we are going to get back around to the tab layout I worked on the tab layout I think a couple of weeks back on stream and there was some kind of infrastructure stuff that needed to get done here and there in a couple of places because I had used it when I created that tab layout and so like I kind of backburnered it for a couple of weeks to to let that stuff actually get PR'd and merged in that way I wasn't writing a library code that's like using stuff that doesn't actually exist yet and you know making a PR and like nobody can really test it unless they also go and get this other thing the good news is though some of that stuff has been merged so I'll take a quick look at what that stuff is in case anybody is interested in the core it was I want to say 6270 that number is sticking in my mind tile grid see if I should have just guessed yeah it was 6270 so this one got merged in the core a couple of days ago this is basically allowing tile grids which are one of the kind of basic units of display IO based you know graphical interfaces tile grid is one of the kind of like building blocks of display applications this was allowing tile grid to be able to have its bitmap changed so before this PR the way that it would work is when you create a tile grid you would give it a bitmap like this and then it would just have that bitmap for the rest of its life cycle it would never really change the bitmap that it had and so what we did is we went and modified inside the core to make it so that you can actually change the bitmap like this you can just say tile grid dot bitmap equals a new bitmap just like this and that will basically swap it so that a different image is now the source image or the spreadsheet is often how I think about it a different image will be the source spreadsheet for your tile grid and we use this in the tab layout in order to change between the the showing tabs and the non-showing tabs so tab layout you know a good way to that most folks are probably familiar with the tab layout is like browsers right I have all these different tabs open here one of the tabs in this case this one right here is the tab that's showing and it has a visually different you know picture right it's it's light gray whereas the rest of these are a darker gray and if I hover on them then they're almost like a medium sort of gray kind of in between the two so you know the ones that are not currently showing those are dark the one that is showing is lighter and then in this case there's also hover effects on some of the other ones so this is the kind of interface that we were making for display IO to be able to create these different tabs show them on the display and then specifically this ability to change the bitmap that is on a tile grid allows us to change that visual picture that represents the tab that way we can have one for the showing tab and it looks different than the others for the non-showing tabs was there a vector IO change for palette I'm not sure the context you mean if you mean anything related to this PR there was not let me just think for a second yeah I don't think there was anything related to this PR for vector IO I guess well vector IO yeah I don't think so maybe vector IO is one of the things that this bitmap can be I actually don't remember that off the top of my head how you how you add a vector IO does it get added to a tile grid or does it have its own tile grid I don't know for sure but nothing specific I don't think came from this PR there was a change like a couple of months ago on vector IO where the palette behavior changed like it used to I want to say the deal was it used to be like the color at index one was the color that would show on the screen and now it's the color at index zero so that changed a couple of months back I say a couple of months that's what it feels like to me I don't know the exact timeline truthfully it might have been longer ago than that that's the last major change with vector IO that I know of though so this is merged that's perfect we can use it for our tab layout the other one is inside of image load which we also ended up using for the tab layout and it is also merged now this one is to add tile grid inflator so when I first started working on the tab layout one of the very first things I did was build this sort of helper function called tile grid inflator and what it does is it takes a three by three spreadsheet and it allows you to inflate it into a larger spreadsheet like this one where it will repeat the center rows and columns so the the three by three spreadsheet if you can imagine it for these sprites here it's got the three walls and then they come down and then they come over and they go back up and there's like a single floor tile in the original spreadsheet I can pull it up github makes it a bit of a pain to get to the actual file we have to like go here and go view file and then go raw and then we can finally see it so this is the three by three spreadsheet that the example uses but this helper function is more generic it can obviously work with any any three by three spreadsheet so it's taking a small three by three like this and then again it's inflating it into a larger rectangle um sprite sheet uh to be able to duplicate those center rows and columns in order to make an arbitrary size palette index is what I was thinking okay yeah that one did change I think a little while back to go from one to zero at the same time there was also some other refactoring that took place as well you used to have to use like vector shape uh was like a class you used to have to like create one of and then I don't know add your add your polygon or your rectangle to it or something like that and I think the new api you don't have to do any of that stuff anymore you just create your shape and then add it to a group maybe make tile grid for it I'd have to look at the docs honestly that's one of the things I don't quite have down perfectly in my head at least to be able to just pull it out the the setup for vector IO I'm getting better with a lot of the display of stuff though like groups and bitmaps and tile grids and stuff I actually am to the point now where I can just write that stuff out without referencing the docs but vector IO I do still have to look so these two things have gotten merged which is great because they're both used by the tab layout so that's what I'm going to do today is go back to working on the tab layout now that we have some of this infrastructure in place and I think it looks like I don't have that version on here let's see well check the other pie portal maybe it's on the other pie portal this is the pie portal titano I say this I don't know if I ever actually showed it but this this right here is the pie portal titano maybe I was doing the development on the the regular pie portal remember for sure let's go find um display a layout let's see it right away let's just go open like we're right there with open even better uh yeah we can just overtake this one so page layout page to layout I make a different branch like it uh okay hopefully it's on the other pie portal um be a bummer if I had gotten rid of that probably I wouldn't have done that I'm gonna switch over to the other pie portal here and really the first thing I should do if we do find it is copy it out of here and put it somewhere else because we shouldn't just have it only on the uh only on this device but I don't think this is updated truthfully oh no this one doesn't even have the page layout we're gonna go back oh I really hope I didn't delete this that is gonna be kind of a bummer if I did this didn't refresh again here seems like man yeah I really hope I didn't do that did I really do that I'm lay out so we're gonna take the sample code of a different lib start like an old lib or something no okay whoa this is actually kind of a pretty big bummer uh two two three one puppy how's it going maybe it's in here this thing keeps onto stuff no dice uh I mean I don't I've never tried vs code so I don't really have a reason for not using vs code other than I already use pi term and I like it and therefore I'm not like interested in trying or using vs code basically um this is all the way back to the 18th these are like individual files a lot of them are codepie does it have history inside here oh please 301 files no this has been a long time since I have just straight up deleted something I didn't mean to certainly looking like this is what's happened though sorry I'm pretty bummed by this actually would I have been using a different device this has to be the device I would have been using well I guess learn this lesson from my pain is uh don't just leave a copy on your device and that's it right like write your code on your computer and then copy it to your device check it into version control if I had done any of those things I wouldn't be in this situation so learn from my mistake and don't don't do that or I should say do not only edit code on your device and do check stuff into version control early and often gosh that is that is uh that is a bummer but I mean I guess then we're gonna remake it I was hoping to like finish it and like I didn't I thought it didn't really have that much stuff left that needed to be done so I was gonna like work on it and try to finish it okay this is maybe our last chance now this is so old it's not gonna be in here this isn't even have tablet out here I mean I'll look but it's not gonna be here I was using the titano temp logs on your computer uh yeah no need to apologize at all I'm a pro any and all potential help from anyone in any way so definitely no need to apologize thank you Liz for the idea needed a time machine to learn from that yeah temp file system so it does have something similar which is this thing that I was going through um which is like a pretty solid history of a bunch of stuff it just didn't uh didn't end up actually being in there on any of the ones that I looked at I even know when I would have done this I was like this is different interesting you know what probably might have happened I'm guessing circup actually might have might have wrecked me in this instance because circup I did circup update all a couple of times and that like caused it to update a bunch of things and I bet you it deleted bet you it deleted my version that was loaded on here where is this I have no idea this is rather long yeah I'll let it keep running back there I think it's gonna find it oh bummer uh sorry this is not very interesting probably to watch me just be miserable about having deleted this on myself like this um yeah I thought crossed my mind as well Dexter I think I did most of the development on stream so I guess I could go find find it and try to copy it from the uh from YouTube no I definitely made it I showed it on show and tell a couple of weeks back yeah I should know better than to not I definitely should know better than to not just have it only on the device no yeah I definitely created it I know for a fact I created it I think I showed it on show and tell a couple of weeks back I showed the the inflator and then real quickly I showed what I was doing with the inflator and the example that I showed at that time was the tab layout and then I think I did do lots of the development on the stream but I don't remember for sure all right well I guess let's start redoing it um let's start by making a branch and working in the branch and then not getting ourselves into this problem again uh new branch tab layout uh I could go back and try to find the try to find the YouTube I don't suspect that's well two things YouTube is annoying and I don't think this does this work I don't know how to find the right page on YouTube basically the gist of it is this YouTube is logged into some different account the other YouTube inside chrome is logged into Adafruit I don't want to mess with that because it could mess up the stream I don't actually know how to get to like my own YouTube page I think this was no this was my YouTube name but I changed it I mean that's like that's a that's my that's the name I'm pretty sure what is it slash you or something channel there we go slash user can you just do this part yeah I don't I don't really want to like well first of all I don't want to look at this as a table I want it to be a list and I can't see the list unless I'm logged in I guess because I can't see the whole title I have no idea how long ago it was deep dive I also don't know some of it might have been on deep dive some of it might have been on my own stream there's tab layout right there and it is on the titano so I think I ended up changing it I saw a moment ago there where it was extending page layout I think I ended up changing it to extending group I guess yeah I don't know sorry this is probably not very interesting let's work on code let's just code we'll just start it over I'll go back after the fact I guess and I'll watch that video and try to match it up with with where I was before but I also don't just want to have you all watch me watch YouTube and copy stuff out of it so yeah thank you redoing it would be faster probably well I mean if I could recover it by just getting the file that would be faster because I could still fix it after the fact recovering it by going through YouTube would take a while though and probably rewriting it would be easier that's definitely true the IDE gives you that false sense of security yeah that it's kind of like a crutch definitely definitely true there oh thank you uh two two three one puppy for the youtube link there what have you lost uh library I have the example actually still interestingly I do still have the example which actually is is nice I mean it could be worse right well obviously it could be worse there's lots of people who have it worse than just accidentally deleting a file let's not get it's not get too you know out of context I guess but like um the the single file here tab layout this is basically what I lost is I had a file here that I wrote that was called tab layout it was inside display layout I do actually still have the test which is nice because one thing is like we'll know if I succeeded in rewriting it if the test still works correctly so um that is actually kind of cool because we at least can know when we are done if we were successful or not um let me just look down here could I know okay I was like is there any chance I could have just had tab layout in the root also but no yeah so library library file single file uh thanks for your openness we've all done this we'll likely do it in yeah that's definitely true definitely true and I appreciate all of you good all of you watching along and sharing in my uh in my misery here slightly but also uh again the greatest part about this and discovering this on the stream is hopefully I can help somebody uh if I ever help any even one person accidentally not do this then like that's amazing and that's awesome so I am grateful for that kind of opportunity to share let's close some windows I did some work on Blinka earlier oh you know what let me uh let's talk about good news for one second here um a couple of weeks back I tried maybe even when I was working on the tab layout actually I started uh trying to run it on this Blinka display IO pie game display which is a thing that uses Blinka display IO to draw on the PC to be able to draw stuff like this so like I have this library um that runs on the PC python and you can run stuff and it basically gives you a display IO window actually discovered a bug earlier where uh we noticed that bitmap label is not working uh but regular label is which is weird I don't know why and I'm gonna look into that later um so ordinarily how this actually looks is actually like this we have label here there's hello world in the middle so uh this basically this pie game display creates a pie game window and then allows Blinka display IO to treat that pie game window as a display like ordinarily you would connect your display over spy or i2c or something like that this has the pie game window and it kind of lets Blinka display IO think it's a a physical display a couple weeks back we tried to run maybe even it was our tab layout I'm not sure it was one of the things I was working on we tried to run it a couple weeks back and it wasn't working and this amazing person on github Schultronix who I will be putting a hug report in for this week but uh sneak preview I guess hug report to Schultronix they went through Blinka which it turned out is where it had gotten broken basically this this functionality ends up getting broken every now and then because it's not really the main purpose of Blinka or even Blinka display IO those things are intended to run on like either uh micropython um microcontrollers or on like single board computers like raspberry pi um but it turns out you can use them for other stuff like this one which is running on pc you know nor normal pc python on a pc not a raspberry pi but like a full laptop or desktop um and so it gets broken every now and then because like there's not that many users and it's kind of just up to me to notice it's broken and fix it uh we we noticed it was broken a couple weeks back and then actually this person asked about it on an older issue where I had fixed it before and I kind of pointed them in the right direction of how how I fixed it in the past they went through and and updated it and put in a pr uh to get it back to working so this does actually work again now if you pull this um the branch from this pr and then I did a left a review on it and then we'll see I'll let I'll let Melissa look at it as well and see if she has any any thoughts or ideas or anything to change but as soon as that gets merged in then the pi game display will go back to working for everybody if you install it like normal so um that's cool we can also do some of our testing on the pc now which is nice because it is quicker uh to iterate generally writing on the pc also if i'm writing on the pc there's a lot more things that backup my code for me which is also kind of apropos at this at this moment in time so that's that's kind of the good news for the day is pi game display is back to working and it's that person on uh on github who did it shuletronics so thank you to shuletronics that's amazing and I really appreciate you working on that even if you're not going to see this um I don't know if this person watches the streams but um so this is going to extend a group and it's going to have a init function I need to learn more about these double dunder functions which is kind of a funny name I think some of the pi contacts I was watching today they were kind of talking about what some of these different things are and like init and like string those are pretty much the only ones I ever use that I can think of off top my head at least but there's actually quite a few more that allow it allow your class to interact with operators differently so there's a lot of neat stuff that you can do with these sort of built-in and they call them dunder functions I guess for the double underscores there that's one of the things uh some of the pi con talks have inspired me to dig a little deeper into what is this yeah there we go give a talk about them at euro python oh I'll have to go watch that hopefully it's on on youtube out there somewhere um so I think we ended up let's see I wonder if my test used all of my let me copy the test into the examples directory so I can see it in the same file yeah let's add it there's a big thing here that's commented out eventually I'll go back and fix that but uh okay so we had x y we needed to declare those or do we get those from group I think we should declare them maybe we should also while we're here or just start adding this stuff and I guess there's probably default zero uh probably not formatting that right yeah there we go let's split it split that thing close this okay so we took a display I guess probably we took a display in order to get the display size which is a display display IO display yeah I think so maybe this would make it not work with the pi game display although if you pass it in then it won't use board so it should still work I think I'm gonna doubt comment it out yeah any resources uh oh is that the one's the question for chipoo let me catch up here at some point I should really read or go through a lot of the concepts of python to fill in the gaps I have from just random coding yeah I feel like I'm kind of the same I've learned a lot of python by using it but not never like formally learned anything just like as I discover stuff get used then that's kind of when I learn about it and sometimes I only learn about it in the context that I saw it used and I don't even necessarily go and read further the book fluent python is a great deep dive into all the dunder methods or magic methods which is definitely a lot cooler name I'll give you that mostly the official docs and peps not sure if a method named uh double underscore would count as magic method or just a method with a mangled name and technically it begins and ends with dunders but it feels off uh okay tab height tab height this one is an int and I guess we could give it a default let's make it default none and if you don't pass it then it will use the height of the image which you do need to pass at least one tab text scale is an int and I think one seems like a fine default uh custom font this is a so I think it's two different possible types right display text whoops because it because I think the built-in font why does this move weird what are we doing here we have shift held you do have shift held okay that's better um what's I about to look up oh display text display text I think the built-in font is a different kind of object if I'm recalling right one thing is I did not actually write type annotations for this the first time around so this part of it will take me longer but will be nice because it will actually have annotations so this yeah there's there's actually three types it can be a built-in font a bdf object or a pcf object which those come from here and default is none so then I guess it's also optional and we need to import union yeah there we go uh let's see in active tab sprite sheet so one thing actually that I want to change about this is that sometimes I called this concept inactive and active and sometimes I called it showing and then implicitly not showing and we shouldn't do that they should all be the same so we should either use active and inactive or we should use showing and not showing and I think not showing sounds weird and there's not is there like a single word that is the opposite of showing there's not really right like inactive is the opposite of active showing I guess hiding hiding is about the best that I can do for a single word let's do a compromise and use three three underscores just keep adding underscores um I think I like active and inactive best so I'm gonna go inactive tab sprite sheet this is gonna be a string but I want it to be able to be an object we made the button so that it could be an object let's try a string first and see if we run into trouble is it string do you do it just string like this and we'll default to none optional I think you will need to I think pass this one will be required basically so we'll check for null and raise an exception if it's we actually got null uh or none rather active tab sprite sheet this one is gonna be the same optional string I mean I guess should this not be optional because it should it's gonna raise an exception if you don't pass a string but it's technically optional because this is gonna set it to none so it's not optional like the user the person who's using this code the person who's calling this function and needs to pass a string or it's gonna raise an exception why is this mad yeah it's not really a reference it's an argument we're creating it okay there's some kind of wrong syntax somewhere what did we do this one is not gray like the rest of these is it just because I have the comma I put something out here oh okay we're missing the comma up here there we go that's weird this one didn't complain actually really weird tried defining a method called 10 underscores yikes hide hidden not sure what hide was a reference to hidden hidden in here too four underscores uh oh hidden for showing the the opposite of shown showing yes sorry thank you hidden and hide yes yes that is correct I think I like active and inactive better than showing and hiding though because it also makes it more clear they're opposite sides of the same coin even though like hidden and showing are similarly but because they don't use the same root base word or whatever you kind of have to know more English do that we're writing the code in English I'm assuming you can speak English but for that reason I do kind of like active and inactive better you don't necessarily have to know as much vocabulary to make the understanding of what it's going to do so I will call this active tab text color and this will be optional int I guess eventually this well it could be a tuple of three ints also because it can be tuple or a single int and I'm not gonna do none actually I'm gonna do I think active we'll do white we do white let's do gray like this I don't know actually why I'm text color showing tab text color oh that's weird I did inactive as just tab text color and then active essentially as the separate case inactive tab text color optional I think probably I wrote tab text color first and then went back and added showing tab text color on G yeah you can use like emojis for variable names I think Scott showed that one time on deep dive before no you can use well I don't know if you're talking about specifically classes I don't know if classes are different than variables you can use I'm pretty sure you can use unicodes for variables unicodes emojis I think you can use emojis for variables in circuit python I have no idea about CPython or any other languages you also need a way to get an emoji into your code file which is non trivial right you have to go copy it from somewhere but unless your IDE has emojis built in we kind of cool this is basically gonna be the same the only differences we're gonna use a different default of white let's do these lowercase okay inactive tab transparent indexes so this is optional and it can either be I'm pretty sure that I created this such that it could either be a a single int or a tuple of ints int tuple oh union tuple of int int and default we'll just do none the next one is actually basically the same active tab transparent indexes the type is the same the default is the same yeah tab count tab count int and I think this one is not not optional really remember why we needed this for some reason we needed to know the count ahead of time maybe to make the size of the tabs yeah maybe that was it so we'll default it to none I guess and then raise an exception if you don't pass it I still don't know should that be considered optional since like it you know it it is none which if it can be none or or the value then that makes it optional but it's not optional because if you don't pass it I plan to raise an exception anyway yeah I think almost just not putting optional is better I'm not sure if it's technically correct but raise dynamite stick could save memory for variable names oh okay I didn't know that you can use them in circuit python but not c python sprite would be represented by the graphics that would be cool a little spaceship emoji or something and that's the variable name that holds your your tile grid with your actual spaceship in it pretty cool all right so we have the init function that's a small portion of what we need I guess pretty early on we're going to be calling super dot underscore init and this is going to be calling group and we're going to pass x equals x and y equals y and I don't think anything else scale equals one but that's default what is the problem here unexpected arguments oh I think is this like this yeah there we go um yeah so now our self is a group after we do that I think we want a like self.tab group we want an underscore equals a new group and we can go ahead actually before we do these things we can check our non optional arguments like I do want you to be able to not have different active and inactive I don't think you should like my recommendation is they should look visually different but from an API standpoint I guess I do want to support you just passing one I don't want to force you to have different ones if you don't want to but you should because they should look different otherwise it's confusing to the user um so I guess technically what's not allowed is having them both be none so if we get past here we know that at least one of them is not none if inactive tab sprite sheet I'm gonna say we could set one into the other one if one of them is no we need to do that maybe we don't need to do that though I think we need to do that if tab count is none phrase must pass tab count so I think the way I structured this before is like I made a private function draw tabs or something like that and then this ends up getting called in order to actually draw the visual sprites for the tabs I think I'm not I think not making an optional unless it's actually optional is clear I definitely agree there in terms of your attentions user wants them to be both the same they could pass the same value uh that's true they could just pass the same you just pass the same we're gonna end up well let me think for one second so the case where it makes like in in the vast majority of cases doesn't really make any difference at all the one case where it does I think make a difference is if they only pass one then we will be able to have a single bitmap and have that bitmap be assigned to all of our tile grids whereas if we just strictly require both we actually are gonna end up with a duplication of that bitmap now whether that is like a big enough problem to worry about I'm definitely open to discussion on maybe it's not but if we force them to pass both of these then we will end up with two bitmaps and if they are if they're actually both the same then we're gonna end up with two of the same bitmaps where we could save that by not having two different copies of it if we know that they're the same maybe we don't care though maybe we should just force them to use both actually because I do strongly recommend like from the user's perspective it's confusing if they don't look different so I strongly recommend they do pass two so maybe we just make them maybe we just make them both maybe we just make them both we just make them both and like you said they can do them both and like I said eventually we'll end up with two copies of the same bitmap and that's not the best but it's also not the worst theoretically these should be relatively small sprite sheets also the idea here with the inflator is your sprite sheets very small you inflate it to a bigger size to match whatever you need would there be references to the same object I mean I haven't written it yet so I guess I could be wrong in my head though I think they end up as two different bitmaps possibly there's a way where they wouldn't and also possibly just I'm wrong in my head that's how I think it would be so one other so another thing we'll have is actually the page layout page layout because tab layout is actually just a page layout and some tabs at the top of it and page layout actually already has all the functionality of like changing between pages showing different pages of content allowing you to set it to page one or two or three or whatever all of that exists inside page layout and so that's what we're using inside tab layout rather than reinventing all of that stuff so we do need a page layout and I think what all do we have to pass to that basic group maybe also we could get the this so this one actually just had an x what about in here given anything different nope still just x y have anything else yep just x y nice and easy all right so then our page layout that's easy we're just going to pass it where are we going to pass it we're not going to pass it our own x y we're going to pass it our own x y no we're going to pass it our own x but y will actually be y plus tab height because the page layout goes underneath the tabs tabs however many there are x y equals y plus I think okay yeah yeah we did do none here so actually before we do this another thing that we will need to do is if tab height was none we need to load our bitmaps because if tab if they pass tab height as none then what we're going to do is just make it equal to the height of the bitmap we actually do we really need them to be able to pass tab height couldn't we just always get it from the bitmap and then what would happen if they gave us a different height than what the size of the bitmap was we do have the inflator so we could technically still support that I think for the sake of simplicity for now actually let's just not let's just not have that and then let's just say we're gonna load the images I don't know the code off the top of my head for I have image load open so many things open I think it's just load and then you pass it the file path a couple of things just that oh but you do get a palette also so we're gonna go do we have to do this before we can start adding stuff to self I don't know and these should be the same size also something else is gonna enforce that I guess I could check it right here but it will eventually get enforced if you try to use two different sizes here it's gonna raise an exception but maybe we can just rely on that raising its own exception rather than actually making a special if statement and raising a different one so we don't need to check the other one we can just use the height of one so now we got that we can do self there we got a page layout good evening from Toronto how's it going Gordy G do we need to do anything else in the initializer not really I think we don't we're not really gonna start calling draw tabs until the user has started calling add content here inside of add content it will call draw tabs but I don't think there is anything else that we need during init uh well actually there is we can go self dot append doesn't really matter which one is on top I mean which one is in front or behind on top the tabs are going to be above in the y axis the page layout but in the z axis the forward and back it doesn't matter because they're not going to overlap so put the tab group and the page layout into our self instance of group um draw tabs I think maybe the code that is in here this one might be kind of similar to what is going to end up going inside of draw tabs actually I have a copy of this over in the repo and I still have the original copy actually on my device as well we haven't changed anything in here so I'm gonna overwrite this there's our tabs I made the all these little lines I was testing how it gets repeated basically oh maybe we should now see this is part of why I changed it I think because this this can now well we're gonna have to fix where this gets imported from the image load version of this you can either pass the string or you can pass the bitmap object bitmap path or object and palette you can do one or the other which is helpful to do it this way because eventually we're gonna want to change the bitmap but this math is actually pretty close to what we need I think this is hard coded from just the size of this sbmp4 must be either 32 by 32 or some multiple of that is actually this file sbmp4 it's 96 by 72 32 is one third of it I'm gonna keep this but it's gonna get changed pretty heavily but it's a good start except for we're not gonna do it just multiple times like this we're gonna we are gonna loop over however many things are inside of our tab layout page content list where I in let's go page object or dictionary in self dot page layout dot page content list which really we shouldn't use it because it's private but really we should also just make it public inside of here it'd be nice to be able to access outside loop over all of those things for each one of them that is where we're gonna create the tab let's just say tab how grid equals imp object will be okay we need to know which one is active let's go I comma in numerator if I equals page layout showing page index then this will be the active bitmap the imp palette will be the active palette target size will be something kind of like this do we want to keep the whole display does it matter really I think what we want is the width and height I'm sure if we care about anything else on the display so the width of the display divided by the number of tabs need to put on self change the thing around here a bit we have tab count here checked it for none so divide the display width by the tab count and then divide that by the tile size of the sprite sheet which is one third of the width of the sprite sheet so that will be active bitmap over three and then we could either use two or three for this we should use three and you should just use a shorter sprite sheet if you want it smaller basically the three means we're gonna match we're gonna use the entire height of our sprite sheet whereas if we were to use two we're actually leaving part of it off so for instance inside of this one we were using two which meant like we were only showing the top two-thirds of this we were not showing the bottom third but I think we should just show the full thing also we used the we used the the height of that for our tab height which means we're building in room for the whole thing so we definitely do want to use it that way but you could of course just make that bitmap smaller right if I just made it two-thirds as tall as it actually was that would also achieve the same visual outcome as having set this to two and using the bigger one okay so do we need to pass anything else doesn't look like it so we get this is our active tab and see this is where I'm not sure so actually yeah we don't want to just create this every time uh see I know I solved this problem before it makes it weirdly extra frustrating to like come across the same problem and know that you solved it but not remember how uh see you the trippin thanks for hanging out so the thing is we don't want to create one of these for every tab every time we call this we only want to create I mean we only we want to create a total of however many pages we have so if we have four pages then we want to create four of them and then we don't need to ever create any more but the intention is actually this gets called after it gets called as part of add content which means it gets called once after the first thing is added it gets called again after the second thing is added again after the third thing again after the fourth thing it's called four times each time it's looping over all the pages you don't actually want that maybe we should have initialized tabs and update tabs but we will also need text which we won't have until add content gets called possibly although I would be creating the only the tabs the pages themselves are all going to be handled inside the page layout so we don't actually have to manage the pages themselves that will all just work but the tabs could add them to a dictionary but what would we use for the key because we don't have the page names until add content has been called I guess we are looping over these objects though so we do have the page names while we're looping because these are basically these things I want to peek so bad back at the YouTube video try to find where I wrote this the first time let's see we don't yeah we can't just loop every time we need some check like if we have already created this one or not then don't do it again I think I also want to just use the inactive for all of them and then change one to active when needed how do we want to do this so I mean we could say something like if the length of the tab group is less than or equal to just less than it comes first the chickens much later so if the length if the number of tabs we have created so far is less than the number of pages it feels so wrong to be doing this inside of this loop though right like we're looping over all the pages and then for each one of those pages we're now saying is the number of tabs we've created so far less than the total number of pages that we're looping over so if you called this before you added any pages well if you if you called it before you added any pages then you're looping over a thing that has nothing in it so so nothing inside here happens let's say after you've added one page but we haven't created the tab for it yet so the length of this will be one zero is less than one so we would go in here we would create this so we create a group for the tab we create a tile grid for the tab we add the tile grid to the group we create a label for the tab we took a font right custom font and actually instead of none we should use this so then we'll say custom font which we didn't put on self the text is the name of the page which will be page dict page name the color will be active tab text color and active we want to keep also and the placement that the placement is tricky I had trouble with this the first time too because basically we want to center the text inside the tab so let's go anchor point is let's call it the middle now let's call it the middle and then the anchored position is much trickier this is what is this well this is our our tab location the width over the tab count that's one fraction of the display but we also then need to multiply that by current one in the loop the first one would multiply it by zero which means that x would stay zero is what we want and then the next one would multiply it by one which would mean where that that many pixels in from the side so now to center the label inside of the tile grid I think we could go tile grid dot x plus half of the width of the tile grid which is actually the width times the tile width because the width itself is in tiles and the tile width is in pixels so the x location of the tile grid plus half half of the width this is the full width this is the full width we actually want half of the width we could divide inside of here I guess please stop climbing the window buddy uh we want to do that though I think we want to divide afterwards I'm actually gonna do another set of parentheses I know I probably don't need all these parentheses but I like them because they make the expression more clear to me although PyCharm just totally didn't type one of them so not really but now they make it more clear this case actually the new lines mess it up pretty bad but basically this should get done first and then divide it and then add it and yes I know again not all of those are needed we could rely on order of operations I like adding extra parentheses instead of relying on order of operations uh is that the right spot maybe it seems like it I guess we need to set the y still that's all the x the good news is actually the y it's not quite as difficult uh is it the y we basically want half of the height of the tile grid which is more or less this but tile height actually we want the y still why I don't know if we want the y or not we need to do the y the plus the plus y we don't in the case that your tabs are at the top of the screen but I guess if you gave it a different y and your tabs are not going to be at the top of the screen but maybe we already handled that by passing it to something super in it I don't actually know how it's going to end up we'll have to see let's try it like this we created a label we put it in a spot hopefully it's the right spot add the label to the group add the group to the tab group which is on self and now the crummy part this was all if it was active which means we're gonna have to duplicate all of this code for the inactive actually sounds pretty terrible and so I don't think I want to do it let's get rid of this let's call this tab group let's call this tab tile grid label and let's actually use the inactive ones but truthfully it's not going to matter because we're going to set it afterwards so we'll create it with inactive we'll do all this stuff and then I guess what we can do is actually just inside of here maybe right before we add it we could just say if it's fixed the invention we could just say could put our statement back if I is equal to self page layout dot showing page index then basically bitmap equals inactive bitmap pixel shader inactive palette it'll do all of this stuff for all of them and then if it is the showing one it will change just what it needs which is the bitmap and the palette we add that to our tab group I guess we shouldn't call this tab group if we have a different tab group uh current or something individual tab group I guess the problem is this is an individual tab group this holds a tab visual which is the tile grid and a tab label and that's it whereas this tab group holds all of the tab groups all those individual tab groups each one with their two things the tile grid and the label I would like better names for those but I'm not going to dwell on it too long right now so I think this would draw the tabs for us um get rid of this stuff for now I think and so let's say uh add content and this if we pass to this thing well it's I guess we'll call it tab content tab content and then tab name tab name and then we're basically just going to turn around and call page layout add content which takes the same two things so we're going to go self.pagelayout.add content the tab content the tab name that's going to add it for us and then we're going to call self.drawTabs that's going to draw our tabs for us I think that is the bare minimum of what we need to at least like run the test and get it to do something anything it's not going to work all the way it's not going to transition between tabs it's not going to change the active tab to have a different visual I mean it will set it up correctly the first time but if we manage to change to a different tab it wouldn't update it all that's fine for now I think this might be the bare minimum we need to just start adding tabs and have it show the first tab to us oh we didn't ever use these things actually yeah tab text scale let's put that on self self.tabTextScale equals tab text scale did we default that to one yeah okay uh let's when we create the label let's when we create the label here we go scale equals tab self.tabTextScale yeah and then also we had what else we didn't use these tab transparent indexes these get passed to inflate tile grid actually here well one of them does actually instead of passing them there I think we should just handle them here because if we pass it to inflate tile grid we're only ever inflating the inactive we're not inflating the active we're just setting the bit map after it's already been inflated so if we passed it in here it's not going to handle the active one for us so if they happen to be different then it's not going to work right so I think here we actually need to go if active transparent indexes is instance is instance object int if it is an int then we are just going to go when was this active active pallet dot make transparent this else if it's a tuple loop over it for index in transparent indexes active pallet make transparent index and we're basically going to do the same thing for and actually why don't we just do else raise attribute active tab transparent indexes must be int or tuple eventually maybe we can come back and allow a list also I don't see any reason why the list wouldn't work but the list implies it will change in size which we're not necessarily really supporting so active okay is this man too many space that should handle our transparencies I don't think we need anything else so let's save this let's copy it to our device and figure out why it doesn't work because I'm sure there's a reason it doesn't work why do we have some imports for this and some imports after this I think that's weird I don't think it matters but I like to do built-in modules before library modules and then I also like to do imports from the library that the code actually lives in at the end after everything else so now I've now I've just changed this so I'm actually going to copy it back and paste it again over right oh right we took that out we changed that floor div oh I probably missed a dot width somewhere probably I'm dividing a bitmap by a number which you can't do obviously what's the line 80 this is the width pixel shader type this is a bitmap not a palette okay getting closer that's just because we started calling like show page and stuff and we can't do that because we haven't implemented it yet I call this test page layout okay not bad could be worse we got a tab I don't know why we didn't get the rest of the tabs it seems like so the tab that we do have has the word one inside of it so I guess the question is do we have more tabs behind it or did this only get called once basically got called four times but I was zero every time okay this is where this gets weird because we're looping over all of them each time yeah this logic is wrong we don't want to know if the number of tabs we have created is less than the number of tabs we need to create because that doesn't take into account the fact that we're always starting on I feel like this is a bad way to do this somehow this needs to involve I we could say if I is less than length of self page layout does that actually work kind of so we did get four tabs this time and all four tabs have the right words in them and they are all in the right locations pretty good but we did do this more times than we should have so it's not actually is I less than the number of things inside the page layout it's actually I less than the number of things in the tab group that can't be right can it because that didn't draw any tabs so I mean I guess we could say and which is not how you write and I don't think this is gonna work either honestly hmm I don't know I mean it showed the right thing like this but it's definitely not what we want because it's creating way more than it needs to it's going like I don't even know what it's doing 0 0 1 0 1 2 0 1 2 3 it's like duplicating first time it got 0 and then 0 1 and then 0 1 2 and then 0 1 2 3 that's wrong but we do end up with what looks correct problem is there's just extra ones back there there's like four of this one three of this one two of this one are all stacked up yes we could say like kind of what Timmy was saying before tab dictionary tab dict pulls new dictionary just take this out altogether if page dict page name not in tab dict then do all this stuff and go page name I guess we could just keep a list of names really of course this does get weird too if somebody has two pages with the same name this is gonna break I guess we'll worry about that later I don't know quite how to deal with that I think this makes it run the right number of times though we did get the right number of times we have our tabs looks like we got our placement done correctly as well our page layout is below our tabs which is nice this is taller than the example I did initially because before I was using that two instead of the three I'll have to go back and just make smaller tabs if I want to do that um we did do this as active instead of inactive so inside here is actually where we should although why did we not change oh I did this wrong there we go that's how it's supposed to look okay so we got all that drawing connect and provide an ID in addition to could go that round I mean technically they have an index inside the page layout which must be unique I guess we could use that might do that in this case the index I guess I mean really it's just an I so we could do like tab dict I equals new tab group then and we would say here if I is not in tab dict because even if they have the same name they will have different indexes so let's make sure that actually works this one said two also if it said two two then that still works correctly I'm about out of steam honestly is unfortunate because we're not even quite back to where I was before when my real goal was to like finish this and make the PR for it but such as life I think um let's at least get a little bit more hooked up so that we can change tabs and then I think I'll call it a night after that so the couple of ways that we change tabs in here were show page which took a page name or a page index but also we did showing page name and showing page index and also next page so one thing is all of these just pass through to page layout it would be really really nice if we could get by without having to actually duplicate all that code but I do not know how but basically basically we're taking all of these functions from page layout and we're putting them inside tab layout although they will have much less code in all of them so show page will well this part's actually still the same we don't need to do this because this is going to happen inside so basically we're just here we're going to say self page layout excuse me page layout dot show page and then we don't need any of the rest of this all this happens inside page layout the other thing I guess we need to do is update so let's make a update active tab which will basically do this stuff you would want to assign the hidden identifier oh sorry I missed actually let me scroll up a bit why let them assign identical names uh I don't know I mean I wouldn't as a user I as a user of this library I personally would not probably make two tabs with the same name but like if I can make it work and if somebody has a use for it then I don't see a need to like disallow them specifically um yeah the hidden identifier that was what Timmy's idea was which is essentially what we ended up doing but it turns out like we don't need to actually add explicitly a different new thing because we already have the index which is already unique for each one I just use that um but yeah I don't know I would not probably ever use two tabs with the same name but at the same time like if it can work then like I'm you know there's no reason for me to disallow it specifically just because I wouldn't do it um so this needs to like get the currently showing page so active index is self dot self dot this and then we basically are gonna go self dot type anymore self dot tab group active index so that's gonna get us a group which is holding our tile grid in our label so zero is our tile grid and we're gonna go dot bitmap equals we need to we need to loop because we need to change the other one back to inactive length of page layout so loop over range length of page layout you don't really need this if I is equal to this then set this to active bitmap pixel shader active palette one is the label so maybe we shouldn't do we not bother with this stuff here now we can keep it so this will be return page layout dot showing page index setting it will be actually we could just still call show page couldn't we personally I hate tabs step one help step two help and two tabs named help you need to class this fine shows the key use cases have been thought about personally I hate tabs kind of like tabs you don't like tabs these tabs in the browser I think tabs are great because they allow you to make more efficient use of the screen real estate you have these tabs a lot like my editor has tabs my browser has tabs next page this one's actually easy we just say but we also need to update afterwards Microsoft tab dialog control was overused that's definitely true back in the windows like xp and before like you would go to control panel and there's like three rows of tabs in some of those like settings those that's excessive that's not good one row of tabs is like enough you shouldn't really go more than that I'm with you there the multiple rows and the way it was and like the the windows settings pages definitely got out of control sometimes um not that like my browser tabs are not out of control like my browser sneak preview like my browser tabs are out of control too but at least it's not multiple rows yeah so we call next page we call update same thing here this is actually pretty easy self dot page layout previous page loop my elves are not typing sometimes that did that before too just did it again doesn't feel like it's like the keyboard is not working it feels like pie charm is eating it for some reason you could actually do most of this stuff now I think even all of these all right see if we got all this right I didn't call it page three prefer pages in a nested tree we added nice it's looking pretty good so I don't know if you notice this green text that's here and another thing maybe we'll do it this way that one gets added after the fact that gets added here so the first time we show the second page that's actually not on it and then we add it afterwards so that it will be on it next time maybe it's zoomed in too far I don't know it's not there yet and then we add it right there and now we start looping through so that just shows like if you want to add a new thing to the active page you can do that uh let me catch up I will say um I prefer pages in a nested tree that way my pages can be grouped and categorized I think scott uses a thing like that in the browser which I do think looks pretty cool um but I don't like how much width it takes up which is not that big of a deal I have a wide screen so like maybe I should just try it um but that's compelling too I think I think scott uses something like that and it's definitely been something like when I first started seeing him use it I was like one day I'm going to give that thing a try and it does seem pretty it does seem pretty uh convenient I'll give him give him that and I'll give you that I guess and the creator of that thing basically was the youtube not catching up I think it's just an eggs way of making another egg no that was catching up okay um I think I'm probably gonna call it a night for now we basically got caught back up to where we were we have our our original test is running unmodified so that means that what we created today is at least the same api is what we created before I'm sure there are some differences like especially the way that I handled this inside draw tabs uh I am almost positive this will not be the exact same way I did it before um especially with regards to checking the dictionary and stuff I don't remember how I solved that last time but this is how I did it this time and it is working we're creating the exact right number of tabs we're showing them all that stuff is working we're able to change through the different tabs so the last thing that is left to do besides probably like documentation I think there's probably missing doc strings and stuff so like that's all something also type annotations we did we did type annotations on a knit but not on like any other functions so all that stuff I would say is missing you know doc string for this there's like documentation stuff missing in terms of functionality I think the main thing that is missing is the touch interaction which is actually what I kind of had intended on sitting down to write today was the touch interaction so right now we just have this looping by calling you know next page um but what we ultimately want is a interactive layout to where you will be able to touch either with your finger like this or obviously with the stylus or something like this and it would go to the page that you touch that's what we are ultimately after here and then the other thing we want to make it easy to do is like if these pages contain more buttons we want to make it easy for you to write code that says like if a button on the active page is getting pressed then like react to it in some way um because like a lot of the button examples that exist they just like check every single button in the whole app if it's being pressed and like we want to limit it to where we're only checking ones that are showing because like you don't want to be able to press a button that's not on the active tab basically so um that's kind of the stuff that I think is left to do I don't know exactly when I will get to that but it's not going to be right now and it's not going to be tomorrow so for folks that don't know I normally stream on uh Saturday mornings also at 10 a.m central time but I am not tomorrow tomorrow I got the day off I'm doing some other stuff so I won't be streaming tomorrow morning uh but normally on Saturday morning at 10 a.m I am also streaming so if you're interested in this kind of stuff you can follow me on twitch you can also if you head to that discord channel in the live broadcast chat I'll always post the links um when I am getting ready to start streaming but again like just repeating again I'm not going to be streaming tomorrow morning so you have to follow me or wait until next week to catch the Saturday stream um I'll probably be back sometime on Sunday I don't know exactly what time it will be but I'll probably stream at some point at least on Sunday um and then probably again sometime on Monday as well so again like follow me on twitch and watch that live broadcast chat uh in the discord if you want notifications well in the discord you won't get notifications but twitch you will I think that's how twitch works I get notifications and email for the people I follow but um yeah thank you to everybody who watched sorry it was uh it dragged a little bit there I kind of just got bogged down and being sad about deleting the code but we we got going eventually we got on a good pace we got this knocked out so it's back to behaving how it was before I'm pretty happy about that even though I am bummed that I deleted it uh actually actually actually copy this let's put it back here let's explicitly go and not make the same mistake again so paste the newest version of this from our device into our repo also we're making a commit starting tab layout and we're pushing it we're pushing it it's gonna fail actions probably I assume I don't care I want another copy of it to be elsewhere not on my device not even on my laptop but inside of github we now have this backed up so we will not run into that problem again with this thing at least um yeah if anybody wants to use that you can find my fork of display layout and it's pushed into there now on its own branch so you'll have to grab it from there if you want to use it before we make the pr and everything but you can do that if you're interested otherwise uh well not really otherwise just generally speaking thank you to everybody who watched thank you to everybody who interacted in the chat thank you to everybody who consoled me about deleting my file again sorry I just kind of sulked about that for a little while but we got going eventually uh and yeah I will be back again not tomorrow but probably sometime sunday and then certainly I'll be back next Friday for deep dive so 4 p.m central time on Friday I'll be back for deep dive always be committing that's how that's the rule I need to live my life by now always be committing uh that's definitely going to be the plan so all right thanks everybody okay