<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[8bit's coding]]></title><description><![CDATA[8bit's coding]]></description><link>https://8bitscoding.io</link><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 23:24:28 GMT</lastBuildDate><atom:link href="https://8bitscoding.io/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Journey into the Terminal: Building the pygamelib UI Module (part 3)]]></title><description><![CDATA[2023: A Coding Odyssey
Introduction
In this article, we'll continue our exploration into the pygamelib's layout system, building on the progress we made in the last installment.
Additionally, I'll introduce the library's first practical widget: the L...]]></description><link>https://8bitscoding.io/journey-into-the-terminal-building-the-pygamelib-ui-module-part-3</link><guid isPermaLink="true">https://8bitscoding.io/journey-into-the-terminal-building-the-pygamelib-ui-module-part-3</guid><category><![CDATA[Python]]></category><category><![CDATA[TUI]]></category><category><![CDATA[terminal]]></category><category><![CDATA[sdk]]></category><category><![CDATA[dev-diaries]]></category><dc:creator><![CDATA[Arnaud]]></dc:creator><pubDate>Sun, 10 Dec 2023 01:24:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1699003462120/0e8ea7e4-54f2-473d-8b64-7afd2316ca3d.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>2023: A Coding Odyssey</p>
<h1 id="heading-introduction">Introduction</h1>
<p>In this article, we'll continue our exploration into the pygamelib's layout system, building on the progress we made in the last installment.</p>
<p>Additionally, I'll introduce the library's first practical widget: the <code>LineInput</code> widget.</p>
<p>And finally, this is it - the ultimate "catch-up" post! In the next article, we're diving headfirst into the most recent and thrilling (well sorta...) developments! 🎉</p>
<h1 id="heading-the-gridlayout">The GridLayout</h1>
<p>Building upon the base layout class defined <a target="_blank" href="https://8bitscoding.io/journey-into-the-terminal-building-the-pygamelib-ui-module-part-2">in the last entry</a> of this series, it is now time to tackle the <code>GridLayout</code>. If the the <code>BoxLayout</code> was a nice "entrée en matière", the GridLayout is a different beast altogether. It is a much more complex layout.</p>
<p>So, what are the differences? Well, first and foremost, we now have widgets with potentially varying dimensions in distinct locations, and this needs to be taken into account.</p>
<p>For example, the layout must identify the largest widget in each row and column that limits the width or height of the entire row or column. Naturally, we'll need to compute or track the overall geometry of the widgets managed by the layout. This also hints at a considerable potential for a performance hit.</p>
<p>Why do I say that? Well, if you start considering the usage of such a layout, you can easily make assumptions about the potential performance impact. Let's think about the actual usage of the grid layout, which a programmer would often need:</p>
<ul>
<li><p>to know the number of rows and columns in the layout,</p>
</li>
<li><p>like all other layouts, to know the list of widgets in the layout,</p>
</li>
<li><p>obviously, add and remove widgets,</p>
</li>
<li><p>manage the geometry of the layout (rows and columns size, spacing between widgets, etc.)</p>
</li>
<li><p>and finally, render the layout and its widgets on the screen.</p>
</li>
</ul>
<p>Unfortunately, there are multiple questionable ways of implementing these features.</p>
<p>If you remember from last week, one of the requirements for all of the layouts is to have a <code>Layout.add_widget(w: Widget)</code> method, that adds a widget to the layout without any other information. This means in this case, that <code>GridLayout</code> will need to be able to find an empty space or to create more space in the grid if needed. More on that later.</p>
<p>You can appreciate that all of these operations require logic. And logic means calculations. Calculations mean impacts on performances.</p>
<p>So, what architecture did I ultimately choose? Well, as usual, I aim to write the least amount of code while considering the big O complexity of my code. I prefer not to maintain internal states that ultimately amount to cached values, primarily because it places a significant maintenance burden on future maintainers. If the code is not properly documented or commented, it becomes increasingly difficult to understand.</p>
<p>That being said, calculating a column width involves iterating through all the rows, checking if a widget is present in the cell, determining the widget's dimensions, and storing the largest width. Performing these steps every time the layout needs to return a column width, 60 times per second, could be too taxing on performance. This is especially true when considering that multiple values need to be calculated in this manner.</p>
<p>Ultimately, I decided to implement a balanced approach that would optimize performance without compromising functionality: by caching the values and employing the observer system to dynamically update these cached values as needed. This method allows for efficient storage and retrieval of the largest widget widths, while also ensuring that the most up-to-date dimensions are used for calculations. By caching the values, the system avoids the need to repeatedly perform the time-consuming process of iterating through all the rows (for example), checking for the presence of a widget in each cell, determining its dimensions, and storing the largest width. The observer system, on the other hand, helps keep track of any changes in the widget dimensions and allows for the dynamic update of the cached values. This combination of techniques effectively addresses the performance concerns associated with calculating multiple values in this manner, while still maintaining the accuracy and responsiveness required for a smooth user experience.</p>
<p>Using the observer mechanic like that is not unlike Qt's signal/slot system (which I love a lot).</p>
<p>Another specificity of that layout is that it requires a row and column to add a widget. Except that, I set a rule that all layouts should have an <code>add_widget(w: Widget)</code> method. This means that the <code>add_widget</code> method of <code>GridLayout</code> should be able to perform correctly without coordinates. I did that by adding a quick test on the coordinates and if even one is <code>None</code>, I do a quick search to look for a free cell in the layout. If none is found, I just add a row to the layout. This means that:</p>
<ol>
<li><p>The layout expands exclusively vertically for now,</p>
</li>
<li><p>I need to add an expansion policy for that layout.</p>
</li>
</ol>
<p>As I previously mentioned, one of the challenges for these layouts is to constrain the rendering surface for the contained widgets. The approach that I choose is to limit the buffer that is given to the widget to render into. This way, even if the widget wants to render in a bigger space, it cannot.</p>
<p>In practice, it means that in the rendering loop, when it's time to defer rendering to widgets, I just give them their reserved space in the buffer and a <code>0,0</code> coordinate:</p>
<pre><code class="lang-python">w.render_to_buffer(
    buffer[
        row + r_offset : row + r_offset + self.__rows_geometry[r],
        column
        + c_offset : column
        + c_offset
        + self.__columns_geometry[c],
    ],
    <span class="hljs-number">0</span>,
    <span class="hljs-number">0</span>,
    self.__rows_geometry[r],
    self.__columns_geometry[c],
)
</code></pre>
<p>The result is quite functional:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1699350192147/1acaf63f-8446-438c-b6c7-078e4e0b63b9.gif" alt class="image--center mx-auto" /></p>
<p>Here, we can see the cyan widget being resized, and the rest of the layout adapting accordingly while respecting the widgets' size constraints. For example, the white widgets have a smaller maximum width than the cyan widget. It works great.</p>
<p>The aspect that still needs improvement is the fact that widgets not entirely outside the layout are still rendered in their entirety.</p>
<p>The issue is quite evident in the previous code snippet: I allow the widget to render on the full width of the column (or height of the row). The fix is simple; it just needs to take into consideration the maximum layout's buffer size.</p>
<p>Another <code>#TODO</code> I guess 😉</p>
<h1 id="heading-lineinput-an-actually-useful-widget">LineInput: An Actually Useful Widget</h1>
<p>The last big addition to the UI toolkit that I added through that PR was the <code>LineInput</code> widget. Because colored squares and rectangles are nice placeholders but at some point, I need to put some real widgets in these layouts (to avoid discovering all the problems at the same time).</p>
<p>If you are wondering, a <code>LineInput</code> widget is a user interface element that allows users to enter and edit a single line of text. It typically includes a text box where the user can type, and may also provide additional features such as a cursor for easy navigation, text selection, and built-in validation. LineInput widgets are commonly used in forms, search bars, and other situations where the user needs to input a small amount of text quickly and efficiently (like a username for example).</p>
<p>So I thought that an input widget would actually tick a lot of boxes in terms of pains in the a*s. Indeed it needs a couple of things that are not needed by colored squares:</p>
<ul>
<li><p>It needs to display interactive content,</p>
</li>
<li><p>It needs to have a focus management system,</p>
</li>
<li><p>It needs a visual cue for the user to know where he is typing stuff (like a cursor),</p>
</li>
<li><p>It is preferable to have a history system somewhere...</p>
</li>
</ul>
<p>That's great because, so far, the UI module lacks a focus management system, completely ignores even the concept of a cursor, and most definitely doesn't have a history management system! We're off to an excellent start!</p>
<h2 id="heading-history">History</h2>
<p>So I got to work, the first thing was the history system. It needed to be generic, i.e.: can manage all sorts of objects, not exclusively strings for example.</p>
<p>The second design decision was to make the <code>History</code> class a singleton. I know that a lot of people dislike the singleton design pattern but with very little argument aside from hopping on the hype train. The only really valid cons that I agree with are the tight coupling and the lack of transparency of the pattern. However, the solutions for these are very often dependency injections. So... Tight coupling anyway, no?</p>
<p>I use many singletons in the pygamelib, mostly for resources that must or can (mostly depending on the user's choice) be shared between many objects and for integrity and performance reasons you will want to manage as a single instance anyway.</p>
<p>In most (if not all) cases, it is up to the user (as in the programmer using the library) to decide if he wants to use a global instance or a specific instance.</p>
<p>Let me skip ahead and give you an example, in my implementation of the <code>LineInput</code> widget, the constructor accepts a history parameter that must hold a <code>History</code> object. If it is not provided, the constructor will try to acquire a global reference to the history object. It is up to the developer to choose to use the global instance, a specific instance or none at all. Another example of a singleton is the <code>Terminal</code> object, this is a common resource managed by a single instance because I want to make sure that nothing else is going to change the state of my terminal while I'm using it (the pygamelib restores the terminal to its previous state when it exits).</p>
<p>Back to the topic, it was my first time implementing a history system \o/</p>
<p>I implemented it by considering an action as a point in time. And I'm moving that action (or object) along the timeline. In practice, I have 2 lists for past and future actions and a scalar for the current action. Then it's just a matter of wrapping the movement along that timeline in actions. From a developer's point of view, using it looks like this:</p>
<pre><code class="lang-python">global_history = History.instance()
global_history.add(<span class="hljs-string">'Hel'</span>)
global_history.add(<span class="hljs-string">'Hello'</span>)
global_history.add(<span class="hljs-string">'Hello Wo'</span>)
global_history.add(<span class="hljs-string">'Hello World'</span>)
print(global_history.current)  <span class="hljs-comment"># print "Hello World"</span>
global_history.undo()
print(global_history.current)  <span class="hljs-comment"># print "Hello Wo"</span>
global_history.undo()
print(global_history.current)  <span class="hljs-comment"># print "Hello"</span>
global_history.redo()
global_history.redo()
global_history.redo() <span class="hljs-comment"># This one does nothing as we called undo only twice.</span>
print(global_history.current)  <span class="hljs-comment"># print "Hello World"</span>
<span class="hljs-comment"># Now an example of reset through add()</span>
global_history.undo()
global_history.undo()
print(global_history.current)  <span class="hljs-comment"># print "Hello"</span>
global_history.add(<span class="hljs-string">"Hello there!"</span>)
print(global_history.current)  <span class="hljs-comment"># print "Hello there!"</span>
global_history.redo() <span class="hljs-comment"># does nothing as the future was reset by add()</span>
</code></pre>
<p>This example comes straight from the <a target="_blank" href="https://pygamelib.readthedocs.io/en/master/pygamelib.base.History.html">documentation</a>.</p>
<p>The obvious downside of that approach is that each "action" represents a full state of the object that is tracked through the history. It is not very memory efficient...</p>
<p>However, this will suffice for now, as I plan to optimize it later. At least we now have a highly adaptable, generic history support in place.</p>
<p>Alright, one down, two to go!</p>
<h2 id="heading-cursor-not-curse-that-is-the-question">Cursor (not curse, that is the question)</h2>
<p>If we are going to have input widgets, we'll inevitably need one or more cursors as indicators for the user.</p>
<p>The <code>Cursor</code> object is not very complex, but some little traps need to be avoided.</p>
<p>The first one is that we need a cursor that adapts to the user's needs. This means that we need to be able to at least customize the appearance, starting position, and blinking behavior. So that's what I coded!</p>
<p>To get a plain, non blinking cursor you just use:</p>
<pre><code class="lang-python">cursor = Cursor(blink_time=<span class="hljs-number">0</span>)
</code></pre>
<p>Simple and efficient. Now, to customize the look of the cursor we just accept a <code>Sprixel</code> in the constructor. Easy. And a blinking delay as well:</p>
<pre><code class="lang-python">cursor = Cursor(
    blink_time=<span class="hljs-number">0.4</span>,
    sprixel=Sprixel(
        <span class="hljs-string">"|"</span>, 
        bg_color=config.input_bg_color,
        fg_color=Color(<span class="hljs-number">255</span>, <span class="hljs-number">255</span>, <span class="hljs-number">255</span>),
    ),
)
</code></pre>
<p>Aside from that, we need to be able to lock the cursor's position when widgets are updating their content but want the cursor to remain in the same position. Not a problem <code>Cursor.lock_position()</code> and <code>Cursor.unlock_position()</code> are here for that. It allows for a dirty optimization: the widgets can have properties/functions that update the cursor no matter what other functions may want. A good example of that is in the <code>LineInput</code> widget (a bit ahead of time): the <code>LineInput.text</code> property, sets the cursor's position to the end of the text. It is unconditional because it cannot know when to move or not the cursor. It falls down to the user of that property to know when and where to move the cursor. Therefore, when <code>Cursor.insert_characters()</code> uses the <code>text</code> property to set the text, it first locks the cursor's position, sets the text, and finally unlocks the cursor's position to calculate the new position. This way, the text is correctly inserted and the cursor is moved to the right position.</p>
<p>That's 2 down!</p>
<h2 id="heading-focus-management">Focus Management</h2>
<p>The last piece of this triumvirate is the focus management system. Such a system is needed to ventilate the different user-generated events to the correct receiver.</p>
<p>And this is where I kind of stopped. I did not stop because I was lazy but because I had no clear idea of what to do. I started by thinking like a C++ programmer: let's create a <code>Focusable</code> interface that would define everything needed and then write an actual manager to handle these. Oh, and let's create an event manager/dispatcher to accurately dispatch keystrokes and other events.</p>
<p>But wait, I have a lot of these. There are already a lot of systems available in the pygamelib for these tasks. So, after a bit of thinking, I added a <code>focus</code> property to <code>Widget</code> and decided to leave it at that. I'm not entirely sure how I will implement a more bundled/streamlined version of a focus management system so for now I decided to experiment. And to experiment, I need to leave room for errors.</p>
<p>This is extremely important to me in any creative process to leave room for experimentation and error. And, unfortunately, any system that I could come up with, however light, would still start to shape my thought process. Therefore, I just implemented the one thing that I'm certain that I'll need: the focus property.</p>
<p>It allows for basic management through a list and an index! Have a look at that example:</p>
<pre><code class="lang-python"><span class="hljs-comment"># Have some global variables</span>
focus_stack = []
focus_stack_index = <span class="hljs-number">0</span>

<span class="hljs-comment"># Now in the update function of my program (called every frame)</span>
<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">user_update</span>(<span class="hljs-params">g: Game, k: blessed.Keystroke dt: float</span>):</span>
    <span class="hljs-keyword">global</span> focus_stack_index
    <span class="hljs-comment"># If the Tab key is hit, we change who's focus property is True</span>
    <span class="hljs-keyword">if</span> k.name == <span class="hljs-string">"KEY_TAB"</span>:
        <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> focus_stack[focus_stack_index].focus:
            focus_stack[focus_stack_index].focus = <span class="hljs-literal">True</span>
        <span class="hljs-keyword">else</span>:
            focus_stack[focus_stack_index].focus = <span class="hljs-literal">False</span>
            focus_stack_index += <span class="hljs-number">1</span>
            focus_stack_index = focus_stack_index % len(focus_stack)
            focus_stack[focus_stack_index].focus = <span class="hljs-literal">True</span>
    <span class="hljs-comment"># And if Tab was not hit, the currently focused widget receives</span>
    <span class="hljs-comment"># the keyboard input</span>
    <span class="hljs-keyword">elif</span> focus_stack[focus_stack_index].focus:
        g.screen.place(str(k), <span class="hljs-number">23</span>, <span class="hljs-number">0</span>)
        <span class="hljs-keyword">if</span> k.name == <span class="hljs-string">"KEY_BACKSPACE"</span>:
            focus_stack[focus_stack_index].backspace()
        <span class="hljs-keyword">elif</span> k.name == <span class="hljs-string">"KEY_DELETE"</span>:
            focus_stack[focus_stack_index].delete()
        <span class="hljs-keyword">elif</span> k.name == <span class="hljs-string">"KEY_HOME"</span>:
            focus_stack[focus_stack_index].home()
        <span class="hljs-keyword">elif</span> k.name == <span class="hljs-string">"KEY_END"</span>:
            focus_stack[focus_stack_index].end()
        <span class="hljs-keyword">elif</span> k.name == <span class="hljs-string">"KEY_ESCAPE"</span>:
            focus_stack[focus_stack_index].focus = <span class="hljs-literal">False</span>
</code></pre>
<p>Now that I look back at it, I actually think that this is pretty much what I would expect from a focus manager. I little more packaged so I can call a method like <code>focus_next()</code> for example. We'll see.</p>
<h2 id="heading-what-about-lineinput">What About LineInput?</h2>
<p>Well, now that all of this is done, <code>LineInput</code> is more or less just a matter of organizing all of these features. So there's nothing really amazing in that class. But it does work, and here it is in action:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1702171177024/4d3cfb57-d5c5-4582-a3ea-df197196948b.gif" alt class="image--center mx-auto" /></p>
<h1 id="heading-conclusion">Conclusion</h1>
<p>It's very interesting to do things, just for the sake of it. Just to learn something interesting.</p>
<p>I'm often reminded of that while coding this module. I find this exercise intellectually refreshing. Being able to really think of solutions while not being constrained by notions like ROI, speed of implementation, etc.</p>
<p>As usual, I hope that you found this article somewhat interesting. Do not hesitate to give me feedback!</p>
<p>Have fun and keep coding!</p>
<p>Arnaud.</p>
]]></content:encoded></item><item><title><![CDATA[Journey into the Terminal: Building the pygamelib UI Module (part 2)]]></title><description><![CDATA[Introduction
In the previous episode, we concluded with the implementation of the Widget class and numerous unanswered questions. Many of these questions revolve around the automatic management of multiple widgets.
In this installment, I will discuss...]]></description><link>https://8bitscoding.io/journey-into-the-terminal-building-the-pygamelib-ui-module-part-2</link><guid isPermaLink="true">https://8bitscoding.io/journey-into-the-terminal-building-the-pygamelib-ui-module-part-2</guid><category><![CDATA[Python]]></category><category><![CDATA[TUI]]></category><category><![CDATA[terminal]]></category><category><![CDATA[terminal emulator]]></category><category><![CDATA[Programming Blogs]]></category><dc:creator><![CDATA[Arnaud]]></dc:creator><pubDate>Mon, 30 Oct 2023 03:39:30 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1698636523892/c81027c4-9752-440f-8af7-f6416b44edf5.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>In the previous episode, we concluded with the implementation of the <code>Widget</code> class and numerous unanswered questions. Many of these questions revolve around the automatic management of multiple widgets.</p>
<p>In this installment, I will discuss the development of the <code>Layout</code> system.</p>
<p>Initially, I intended for this entry to be the final one, summarizing previous developments. However, due to falling quite ill this week, I was unable to edit the vast amount of material I have on the topic. As a result, I plan to publish one more entry before catching up to real-time. Sorry about that.</p>
<h1 id="heading-the-state-of-things">The state of things</h1>
<p>At this point, we have a functional foundation for a widget with an effective size management system.</p>
<p>Here's a small program I used to test it:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> pygamelib.engine <span class="hljs-keyword">import</span> Game
<span class="hljs-keyword">from</span> pygamelib.gfx.ui <span class="hljs-keyword">import</span> (
    Widget,
    UiConfig,
)
<span class="hljs-keyword">from</span> pygamelib.constants <span class="hljs-keyword">import</span> (
    EngineConstant,
    EngineMode,
)


<span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">uu</span>(<span class="hljs-params">g: Game, k, dt: float</span>):</span>
    <span class="hljs-comment"># Get the widget (because we know where it is, obviously...)</span>
    w: Widget = g.screen.get(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>)

    <span class="hljs-comment"># Delete potential labels</span>
    g.screen.delete(<span class="hljs-number">0</span>, <span class="hljs-number">1</span> + round(w.width / <span class="hljs-number">2</span>))
    g.screen.delete(<span class="hljs-number">1</span> + round(w.height / <span class="hljs-number">2</span>), <span class="hljs-number">2</span> + w.width)

    <span class="hljs-comment"># Handle key stroke to change the widget size</span>
    <span class="hljs-keyword">if</span> k == <span class="hljs-string">"Q"</span> <span class="hljs-keyword">or</span> k.name == <span class="hljs-string">"KEY_ESCAPE"</span>:
        g.stop()
    <span class="hljs-keyword">elif</span> k == <span class="hljs-string">"H"</span>:
        w.height += <span class="hljs-number">1</span>
    <span class="hljs-keyword">elif</span> k == <span class="hljs-string">"h"</span>:
        w.height -= <span class="hljs-number">1</span>
    <span class="hljs-keyword">elif</span> k == <span class="hljs-string">"W"</span>:
        w.width += <span class="hljs-number">1</span>
    <span class="hljs-keyword">elif</span> k == <span class="hljs-string">"w"</span>:
        w.width -= <span class="hljs-number">1</span>
    <span class="hljs-keyword">elif</span> k == <span class="hljs-string">"d"</span>:
        w.width -= <span class="hljs-number">1</span>
        w.height -= <span class="hljs-number">1</span>
    <span class="hljs-keyword">elif</span> k == <span class="hljs-string">"D"</span>:
        w.width += <span class="hljs-number">1</span>
        w.height += <span class="hljs-number">1</span>

    <span class="hljs-comment"># Add the current widget's dimensions to the screen</span>
    g.screen.place(<span class="hljs-string">f"<span class="hljs-subst">{w.width}</span>"</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span> + round(w.width / <span class="hljs-number">2</span>))
    g.screen.place(<span class="hljs-string">f"<span class="hljs-subst">{w.height}</span>"</span>, <span class="hljs-number">1</span> + round(w.height / <span class="hljs-number">2</span>), <span class="hljs-number">2</span> + w.width)

    <span class="hljs-comment"># Update the screen</span>
    g.screen.update()


<span class="hljs-keyword">if</span> __name__ == <span class="hljs-string">"__main__"</span>:
    <span class="hljs-comment"># Create the instances of the Game and the unified UiConfig</span>
    g = Game(
        mode=EngineMode.MODE_REAL_TIME,
        player=EngineConstant.NO_PLAYER,
        user_update=uu,
    )
    config = UiConfig.instance(game=g)

    <span class="hljs-comment"># Create a widget with</span>
    w = Widget(<span class="hljs-number">30</span>, <span class="hljs-number">15</span>, <span class="hljs-number">10</span>, <span class="hljs-number">4</span>, <span class="hljs-number">60</span>, <span class="hljs-number">40</span>, config=config)

    <span class="hljs-comment"># Place the widget on screen</span>
    g.screen.place(w, <span class="hljs-number">1</span>, <span class="hljs-number">1</span>)

    <span class="hljs-comment"># Run the event loop</span>
    g.run()
</code></pre>
<p>The results are mindblowing: it's a square...</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698457830109/5e513088-ed7b-4563-ae0f-7534d001f797.gif" alt class="image--center mx-auto" /></p>
<p>But, as you can test yourself, a square with functional size management.</p>
<p>As observed last week, we soon began asking questions about positioning, automatic size management, and so on. Having experience with various UI frameworks (such as Qt, GTK, and ImGui), I understand that if we have a widget system, we also need a layout system. So, let's tackle that beast!</p>
<h2 id="heading-layouts-where-problems-begin">Layouts: where problems begin!</h2>
<h3 id="heading-the-problem">The problem</h3>
<p>In any decent UI framework, you will find a concept of layout. These layouts are incredibly practical, as they enable much faster development of UI projects by handling many of the geometry calculations needed in various situations.</p>
<p>If you recall last week's installment, my goal is to solve the problem independently. I'm not interested in examining existing code, as the challenge itself <em>is my objective</em>. However, I also mentioned that I would loosely base my UI toolkit on Qt.</p>
<p>There are two reasons for this. First, I have extensive experience coding with Qt. As a result, whether I like it or not, its design philosophy will influence me. Second, I believe that Qt's architecture is the most sensible among UI toolkits. However, that is the extent of the inspiration. Drawing inspiration from the implementation itself wouldn't make much sense, as Qt is a much larger and more sophisticated framework.</p>
<p>That being said, the layout system must be capable of managing the standard operations of a typical layout system. That means:</p>
<ul>
<li><p>Managing sub-widgets (adding/removing widgets to and from the layout)</p>
</li>
<li><p>Keeping count of widgets in the layout.</p>
</li>
<li><p>Managing geometry</p>
</li>
<li><p>Enforcing size constraints</p>
</li>
<li><p>Guaranteeing the access to the list of managed widgets.</p>
</li>
</ul>
<p>That is the base set of core features that all layouts will share.</p>
<h3 id="heading-identifying-the-key-components">Identifying the key components</h3>
<p>For the initial implementation of the pygamelib's UI framework, I am focusing on a select few practical layouts. I prefer having a concise list of functional and useful layouts rather than an extensive list of partially implemented ones.</p>
<p>So, we will have:</p>
<ul>
<li><p><code>Layout</code>: The foundational (mostly virtual) class that establishes the basic API.</p>
</li>
<li><p><code>BoxLayout</code>: A layout that presents all widgets in a list, either horizontally or vertically.</p>
</li>
<li><p><code>GridLayout</code>: A layout designed to arrange all widgets in a grid format (quite unexpected, isn't it?)</p>
</li>
<li><p><code>FormLayout</code>: a layout to quickly create a form-type complex widget. It will organize the widgets one by line and each line has a label (a line/row is composed of a label and a widget).</p>
</li>
</ul>
<h2 id="heading-lets-get-coding">Let's get coding!</h2>
<h3 id="heading-creating-a-basic-layout-class">Creating a basic Layout class</h3>
<p>For the <code>Layout</code> class, I went with something very simple. It is an instantiable object (as Python does not really support pure virtual classes), but only a handful of properties are implemented (as it should be the same requirements for all the layouts). We have:</p>
<ul>
<li><p>parent [property]: store and return the parent widget.</p>
</li>
<li><p>spacing [property]: store and return the spacing between widgets. Note that some layouts may need more detailed spacing information (like vertical or horizontal) but again: this is the bare minimum.</p>
</li>
<li><p>and, obviously, a basic constructor.</p>
</li>
</ul>
<p>For the virtual methods, we need to think a little about what is common to all the layouts. Quite simply, all layouts will need the ability to:</p>
<ul>
<li><p>Add widgets.</p>
</li>
<li><p>Remove widgets.</p>
</li>
<li><p>Count how many widgets they are managing.</p>
</li>
<li><p>Return a list of widgets that they are managing.</p>
</li>
<li><p>Return their total height (including spacing and potential padding).</p>
</li>
<li><p>Return their total width (also including spacing and potential padding).</p>
</li>
<li><p>Render to the frame buffer.</p>
</li>
</ul>
<p>Padding is not supported or even on the roadmap for the first version. But it'll come!</p>
<p>Now as I write that and look at the code, I realize that there is no <code>Layout.remove_widget(w: Widget)</code> method in <code>Layout</code>... That is a problem. But I know why it's like that: it's because it feels useless!</p>
<p>Let me elaborate. In most UI frameworks, there is usually some sort of remove function that allows you to remove a widget based on its pointer or reference. Interestingly, I have never actually used it!</p>
<p>In a box or form layout, I use the widget's index. In a grid layout, I use the row and column coordinates. I understand that I shouldn't impose my own practices on other programmers, but it seems even more pointless than this entire project! Why? Because, obtaining a widget's reference when a user clicks on it is simple, yet maintaining the focus stack in a keyboard-based terminal interface will inevitably lead to using indexes or coordinates.</p>
<p>Nonetheless, I will add a TODO note to implement a virtual method in <code>Layout</code>.</p>
<p>The documentation is already available on Readthedocs: <a target="_blank" href="https://pygamelib.readthedocs.io/en/master/pygamelib.gfx.ui.Layout.html">pygamelib.gfx.ui.Layout</a>.</p>
<h3 id="heading-the-boxlayout-class">The BoxLayout class</h3>
<p>Same as <code>Layout</code>, the documentation is already available: <a target="_blank" href="https://pygamelib.readthedocs.io/en/master/pygamelib.gfx.ui.BoxLayout.html">pygamelib.gfx.ui.BoxLayout</a>.</p>
<p>I'm sure that I don't need to be explicit about that but, I also mean that the code is on <a target="_blank" href="https://github.com/pygamelib/pygamelib">GitHub</a>.</p>
<p>The <code>BoxLayout</code> is quite simple to conceptualize: it essentially consists of a list of widgets. These widgets are rendered either horizontally or vertically. Unsurprisingly, I initially believed that implementing it would be a walk in the park...</p>
<p>To be fair, the first iteration was easy: add widgets, render them vertically. Easy, peasy. Then, take into consideration the spacing. Not very difficult.</p>
<p>Then things started to go south... Adding orientation management is not that hard, but it is taxing to check on the orientation every frame. I came up with a solution that, for the moment, feels like the least of many evils.</p>
<p>I maintain 2 offset variables during the rendering loop: one for the row and one for the column (the terminal coordinate system), and I increase only the correct one depending on the orientation property. It limits the tests to just one <code>if</code>. I then simply render the widget at the coordinate plus the offset. It is basic geometry but it works fine.</p>
<p>Adding size constraints becomes more interesting. At first, and it is the current implementation, I just deferred all of the work to <code>Widget</code>! Remember, <code>Widget</code> does that very well already.</p>
<p>So I decided that the solution was easy: when the user changes the size constraints of the layout, pass them down to all the widgets. Easy!</p>
<p>The code look like that:</p>
<pre><code class="lang-python"><span class="hljs-meta"> @size_constraint.setter</span>
 <span class="hljs-function"><span class="hljs-keyword">def</span> <span class="hljs-title">size_constraint</span>(<span class="hljs-params">self, data: constants.SizeConstraint</span>) -&gt; <span class="hljs-keyword">None</span>:</span>
     <span class="hljs-keyword">if</span> isinstance(data, constants.SizeConstraint):
         self.__size_constraint = data
         <span class="hljs-keyword">for</span> w <span class="hljs-keyword">in</span> self.__widgets:
             w.size_constraint = data
</code></pre>
<p>And it does the job perfectly. Not only that, but it is also efficient as we update the constraints only when necessary. Job done, thank you very much, and push to production!</p>
<p>But wait... There's just one small problem: what about widgets that are added after a change? Well... Then it just doesn't work. And that is another TODO note that I need to add to the code following this article... In that case, you would have widgets with different size constraints that lives in the same layout. It's not hard to fix, it does not even need a comparison or anything: when a widget is added, we need to overwrite its size constraint with the layout's one.</p>
<p>Aside from that minor inconvenience, I'm quite pleased with the progress of this layout system. Thus far, it appears to be versatile enough. The API is also fairly straightforward, as I extensively utilize Python's properties for setter and getter methods. This makes assigning a layout to a widget as simple as:</p>
<pre><code class="lang-python"><span class="hljs-comment"># The number in Widget's constructor are: width, height, minimum_width,</span>
<span class="hljs-comment"># minimum_height,maximum_width, maximum_height.</span>
my_widget = Widget(<span class="hljs-number">30</span>, <span class="hljs-number">15</span>, <span class="hljs-number">10</span>, <span class="hljs-number">4</span>, <span class="hljs-number">40</span>, <span class="hljs-number">20</span>, config=config)
my_widget.layout = BoxLayout()

<span class="hljs-comment"># And then simply add widgets to the layout using add_widget()</span>
my_widget.layout.add_widget(
    Widget(<span class="hljs-number">6</span>, <span class="hljs-number">3</span>, <span class="hljs-number">3</span>, <span class="hljs-number">1</span>, <span class="hljs-number">8</span>, <span class="hljs-number">4</span>, bg_color=Color.random(), config=config)
)

<span class="hljs-comment"># Orientation and size constraint can be set with simple properties</span>
my_widget.layout.orientation = constants.Orientation.VERTICAL
my_widget.layout.size_constraint = constants.SizeConstraint.DEFAULT_SIZE
</code></pre>
<p>And despite its simplicity, it works pretty well! I'm sure that you're dying for more screenshots of colored rectangles so here it is!</p>
<p>The current <code>BoxLayout</code> handle orientation changes with and without spacing between widgets:</p>
<p>Without spacing</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698634643298/7af4eb05-6c5e-423c-a419-b27f81f30ff5.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698634659949/64705d22-4f17-4d1e-8488-56e468ab4e91.png" alt class="image--center mx-auto" /></p>
<p>And with spacing</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698634700520/e7a35f00-c0eb-48f9-92cb-3faf5df4a6aa.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698634711149/43398d1b-4c69-4eb5-aab1-2a6ac60a14b6.png" alt class="image--center mx-auto" /></p>
<p>It also gracefully handles adding non-trivial widgets, i.e: widgets that are already a composition of multiple widgets held into another layout (layout-ception!):</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698634810221/a7236e6d-42af-4bb4-9b4a-58f608b93606.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698634821361/744c4c87-e1e6-4f5b-9c57-39fae71b4712.png" alt class="image--center mx-auto" /></p>
<p>Finally, I have also incorporated culling into the render loop to prevent rendering sub-elements that are no longer within the frame. This completely skips the rendering of elements that are entirely outside the rendering surface.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1698635212910/384f9e9c-2222-4e2f-a757-587355f5dc67.png" alt class="image--center mx-auto" /></p>
<p>Oh, and I realized that there was no way to remove any widgets (even using their IDs) in the <code>BoxLayout</code> class... So I guess that's one more TODO...</p>
<h2 id="heading-closing-word">Closing word</h2>
<p>This entry is already quite long, so we'll wrap things up for today. As I mentioned in the introduction, the next entry will be the last about the past before we switch to the PR, I'm working on right now.</p>
<p>Next time we will talk about the GridLayout and an actually useful implementation of a <code>Widget</code>: the <code>LineInput</code> widget.</p>
<p>I hope you find this somewhat interesting. Do not hesitate to give me feedback!</p>
<p>Have fun and keep coding!</p>
<p>Arnaud.</p>
]]></content:encoded></item><item><title><![CDATA[Journey into the Terminal: Building the pygamelib UI Module (part 1)]]></title><description><![CDATA[Introduction
A bit of context: I've been working for some years now on a Python library called the pygamelib. A not-so-small library to write games and applications in the terminal in Python. If you are wondering, it has nothing to do with Pygame and...]]></description><link>https://8bitscoding.io/journey-into-the-terminal-building-the-pygamelib-ui-module-part-1</link><guid isPermaLink="true">https://8bitscoding.io/journey-into-the-terminal-building-the-pygamelib-ui-module-part-1</guid><category><![CDATA[Python]]></category><category><![CDATA[terminal emulator]]></category><category><![CDATA[Programming Blogs]]></category><category><![CDATA[Terminal-User-Interface]]></category><category><![CDATA[TUI]]></category><dc:creator><![CDATA[Arnaud]]></dc:creator><pubDate>Sat, 21 Oct 2023 04:00:10 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1697991726050/aaba8b9d-76c4-404e-b77f-f44a99761ff4.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h1 id="heading-introduction">Introduction</h1>
<p>A bit of context: I've been working for some years now on a Python library called the <a target="_blank" href="https://github.com/pygamelib/pygamelib">pygamelib</a>. A not-so-small library to write games and applications in the terminal in Python. If you are wondering, it has nothing to do with Pygame and I realized way too late that people would mistake the 2... Sorry, I guess?</p>
<p>This library was originally developed for the kids in the coding classes that I'm giving for fun to young kids from 6 to 15 years old. It started as a rough bundle of classes to make simple games in the terminal (to keep the kids interested).</p>
<p>But it got out of hand... Seriously out of hands.</p>
<p>First, I started to be interested in the algorithmic problems and many optimization issues raised by the needs of such a framework.</p>
<p>Then I got interested in game engine theory (read <a target="_blank" href="https://a.co/d/jhGByHt">Game Engine Architecture</a> if you're interested in that) and soooo many more things...</p>
<p>Many years later, here I am, starting a brand new module in the library: the pygamelib.gfx.ui module.</p>
<p><strong>This series is about the challenges and my approach to developing that UI module.</strong></p>
<p>I'll start with my philosophy about this whole endeavor and I'll try to have regular entries about my progress and frustrations.</p>
<h2 id="heading-philosophy-embracing-the-futility">Philosophy: Embracing the Futility</h2>
<p>Let’s start with a harsh truth: <strong>what I'm building is utterly unnecessary</strong>. Established Python libraries like <a target="_blank" href="https://docs.python.org/3.10/library/curses.html">curses</a>, <a target="_blank" href="http://urwid.org/">urwid</a>, <a target="_blank" href="https://github.com/pfalcon/picotui">picotui</a>, and my personal favorite, <a target="_blank" href="https://www.textualize.io/">Textual</a>, have already mastered TUI (Terminal User Interface) frameworks.</p>
<p>So a new one is not only not needed, but more importantly a dilution of the development effort toward a solid TUI (Terminal User Interface) framework.</p>
<p>So of course I embarked on this exercise in futility! Because: why not?</p>
<p>Before I began, I set some ground rules:</p>
<ol>
<li><p>I want to solve the problems myself. I'm doing that for fun and to learn, so there's no point in looking for answers.</p>
</li>
<li><p>Errors are ok. I will mark that module as alpha code for a loooong time. So let's fail!</p>
</li>
<li><p>Obviously, contributions and feedback are accepted and even encouraged. However, there are probably very few people who have the same weird drive to implement stuff just for fun.</p>
</li>
<li><p>I like the Qt framework so I am loosely going to get inspiration from it.</p>
</li>
</ol>
<h1 id="heading-pr-221-the-beginning">PR #221: The Beginning</h1>
<h2 id="heading-well-not-really">Well... Not really.</h2>
<p>Ok, it's kinda dramatic to call it the beginning because there was already a first draft of a UI toolkit before that PR (Pull Request), and it could already do very nice things:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1697754668160/b44e96a9-d980-4297-8414-b5b26c02f3c0.png" alt="The pygamelib sprite editor 1/2" class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1697754728027/73036707-b1f3-47ae-8759-d32092b12e59.png" alt="The pygamelib sprite editor 2/2" class="image--center mx-auto" /></p>
<p>This is the pygamelib sprite editor (yes: we have a sprite system and even a sprite editor!). Obviously, the library is already capable of having menus with floating displays, dialogs, panels, etc.</p>
<p>The issue is that all of these elements are independent and not really working together. It is functional but not really nice. So let's build something that can help bring all these elements together!</p>
<p>PR #221 is actually <em>that</em> beginning.</p>
<h2 id="heading-the-basics">The basics</h2>
<p>The pygamelib has a fairly simple rendering loop, we have a Screen object on which we can place stuff (game board, UI elements, text, etc.) and <code>Screen.update()</code> take care of triggering the rendering loop (if needed). Each object placed on the screen must either be a printable character, a Sprixel (like a pixel but for the terminal), or implement a <code>render_to_buffer(...)</code> method. All of that is rendered into a frame buffer, and finally, <code>update()</code> just push all of that buffer to the terminal's screen.</p>
<p>Obviously, the UI module needs to integrate into that system and take it into consideration. As a matter of fact, it will be crucial in the future when we talk about geometry... But let's keep that for the next issue.</p>
<p>At first, I was very enthusiastic about the amount of work needed, it did not look like it was going to be horribly difficult. The boldness and pretention of that thought...</p>
<p>For a little context, I started to work on PR#221 on the 19th of November 2022 (well, a bit before that, it was the first commit), the PR was merged on the 11th of October 2023! Admittedly I got distracted along the way but 4025 additions and 54 commits later, I kind of realized that it was not going to be a quick endeavor.</p>
<p>The first thing that I implemented was the Widget class. When rendered on screen it's not much, barely a colored rectangle. But when you look at its logic it's more interesting. So the goal is that the Widget class is going to be the base of almost all elements in the UI module. Therefore it needs to be very generic and keep its logic to the minimum. And that it does, the Widget class does only a limited amount of things:</p>
<ul>
<li><p>it manages its own geometry (max/min height and width, current height and width),</p>
</li>
<li><p>it manages the size constraint policies (<em>à la</em> Qt),</p>
</li>
<li><p>it keeps track of its parent,</p>
</li>
<li><p>it keeps track of its children,</p>
</li>
<li><p>it possesses attributes to manage some internal states (like the focus),</p>
</li>
<li><p>finally, it can hold a layout (more on that later).</p>
</li>
</ul>
<p>Navigating through default behaviors set by the user was as smooth as finding a cookie jar in a kid's room. Nothing hard, but I have a newfound respect for all the people who give us such sensible default values in the libraries that we use daily...</p>
<p>Now here I am, ending up proudly with a square on my screen... cool cool...</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1697757490586/9f635008-147f-4151-95e9-746a71e8be40.png" alt="First widget" class="image--center mx-auto" /></p>
<p>Not underwhelming but far from exceptional. The code to get there is fairly straightforward:</p>
<pre><code class="lang-python"><span class="hljs-keyword">from</span> pygamelib <span class="hljs-keyword">import</span> engine
<span class="hljs-keyword">from</span> pygamelib.gfx <span class="hljs-keyword">import</span> ui

<span class="hljs-comment"># Create instances of the game engine and the UI configuration</span>
g = engine.Game.instance()
uic = ui.UiConfig(game=g)

<span class="hljs-comment"># Place some stuff on the screen</span>
g.screen.place(<span class="hljs-string">"Here is a widget!"</span>, <span class="hljs-number">2</span>, <span class="hljs-number">2</span>)
g.screen.place(ui.Widget(widht=<span class="hljs-number">10</span>, height=<span class="hljs-number">5</span>), <span class="hljs-number">3</span>, <span class="hljs-number">2</span>)
g.screen.place(<span class="hljs-string">"Woohooo..."</span>, <span class="hljs-number">9</span>, <span class="hljs-number">2</span>)

<span class="hljs-comment"># Update the screen</span>
g.screen.update()
</code></pre>
<p>Now, immediately a certain amount of questions arise:</p>
<ul>
<li><p>What if I want to put multiple widgets on the screen and have them organized (but I don't want to spend my time calculating height, width, and positions for each and every one of them)?</p>
</li>
<li><p>What if I want to build complex widgets with widgets inside widgets?</p>
</li>
<li><p>What if I want something else than a bluish square?</p>
</li>
</ul>
<p>All of these are very valid questions, I know: that's the questions I asked myself ;)</p>
<p>We'll talk about that in the next installment!</p>
<h1 id="heading-conclusion-for-now">Conclusion (for now)</h1>
<p>This initial installment serves as an introduction, offering context and a glimpse into the inception of my UI module venture. The next article should offer a more technical dive into the details of this development.</p>
<p>Clearly, I'm documenting a year-long development process, so I'll summarize certain aspects and present it more as a report.</p>
<p>In the meantime, I'm noting things in my coding diary for future articles. I hope that this will be an entertaining window into the hobby of a coder for fun and why not: maybe have little interactions with like-minded people along the way!</p>
<p>Have fun and keep coding!</p>
<p>Arnaud.</p>
<p>PS: I used AI to reformulate some parts (sorry but I am not a native English speaker). Tell me if these parts are obvious ;-)</p>
]]></content:encoded></item><item><title><![CDATA[Hello Hashnode!]]></title><description><![CDATA[Introduction
Hello Hashnode, I am a new contributor to the platform. As this is my first article, let's take a moment for a brief introduction.
I'm a not-so-young-anymore developer and entrepreneur. Not so young, but I like my age: I'm 42 years old, ...]]></description><link>https://8bitscoding.io/hello-hashnode</link><guid isPermaLink="true">https://8bitscoding.io/hello-hashnode</guid><category><![CDATA[introduction]]></category><category><![CDATA[Python]]></category><category><![CDATA[Open Source]]></category><dc:creator><![CDATA[Arnaud]]></dc:creator><pubDate>Fri, 20 Oct 2023 23:43:09 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/stock/unsplash/liWlhUFWp2w/upload/af36ee274465faf947ed22c467ccc136.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<h2 id="heading-introduction">Introduction</h2>
<p>Hello Hashnode, I am a new contributor to the platform. As this is my first article, let's take a moment for a brief introduction.</p>
<p>I'm a not-so-young-anymore developer and entrepreneur. Not so young, but I like my age: I'm 42 years old, which means that I always have the answer 😉.</p>
<p>I like to mention that I began contributing when freshmeat.net was still in its infancy (I recall the early days, even the unreal ones...) about 20+ years ago (I know: 20++). Anyway, let's just say it wasn't yesterday. Over the years, I've contributed to numerous open-source software projects, some big and some small. I've also started and abandoned even more projects!</p>
<p>Today my main project is a Python library called <a target="_blank" href="https://github.com/pygamelib">pygamelib</a>. It's a little framework to make 2D games and applications in the terminal. It's fun, mostly useless but fascinating to develop nonetheless.</p>
<h2 id="heading-why-hashnode">Why Hashnode?</h2>
<p>Well, I sort of rage quit WordPress... There are a few good reasons for that, but mostly it saddens me to see one of the largest open-source successes transform into a greedy company with predatory practices. Don't get me wrong: I'm not an idealist, and I understand that companies need to make money, but hiding meta tag modifications behind a $25/month paywall is a bit excessive. Just a bit...</p>
<p>Hashnode appears to be a more appealing alternative, offering a less cluttered, more efficient, markdown-based platform, along with an enticing free tier that <strong>encourages</strong> you to support them rather than <strong>compelling</strong> you to pay for basic features.</p>
<p>The community also appears to be quite interesting.</p>
<h2 id="heading-my-interests">My Interests</h2>
<p>I am a seasoned developer with a strong background in various programming languages, including Python, C++, Perl, and Java. My experience with the Qt framework has allowed me to create robust and efficient applications across multiple platforms.</p>
<p>I dabbled in many things from system/kernel programming to game development, with a detour into microservices and highly redundant databases. A lot of fun!</p>
<p>At the moment, I'm not coding professionally, so I can indulge in what I enjoy doing rather than what I'm obligated to do! As a result, you'll find a plethora of information on the pygamelib, my insatiable passion for algorithms and applied mathematics (including AI), game development, and any other subject that captures my interest at the moment. In short, just an old man rambling 😉.</p>
<p>Oh, and I can be quite opinionated.</p>
<p>Apart from programming, I am an avid Science-Fiction reader, a passionate amateur astronomer, and a technical scuba diver. Who knows, this may influence some of my articles.</p>
<h2 id="heading-what-to-expect">What to Expect?</h2>
<p>Well, I plan to write several articles per month. I've begun a series documenting my journey in developing a user interface framework for the pygamelib, and I hope it will provide engaging content!</p>
<p>I am considering writing vulgarization articles about some algorithms that I enjoy. Perhaps a bit of AI basics, just to assist those who believe they know everything there is to know about AI in understanding what an activation function is 😈.</p>
<p>I also want to share tutorials and how-tos about topics I enjoy and wish to share with others.</p>
<p>You won't find any "industry analysis" or "case studies" here. This is my personal blog and I want it bullshit free.</p>
<h2 id="heading-upcoming-content">Upcoming Content</h2>
<p>The upcoming article will be the first installment in my series, "Building the PygameLib UI SDK: A Coding Odyssey." I hope you find it interesting.</p>
<p>Over time, I would be happy to get feedback from the community!</p>
<h2 id="heading-conclusion">Conclusion</h2>
<p>I am deeply grateful for the opportunity to embark on this exciting journey, sharing my knowledge and experiences with the community. I look forward to connecting with like-minded individuals, learning from each other, and growing together as we explore the ever-evolving world of technology.</p>
<p>I invite you to connect with me on Mastodon at <a target="_blank" href="https://floss.social/@8bitscoding">@8bitscoding@floss.social</a>, where we can continue our discussions, exchange ideas, and collaborate on projects. Let's build a strong and supportive community together in the realm of open-source technology and beyond.</p>
<p>Until next time. Keep coding, keep having fun!</p>
]]></content:encoded></item></channel></rss>