<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ilean Monterrubio Jr</title>
    <description>The latest articles on DEV Community by Ilean Monterrubio Jr (@elcapitan).</description>
    <link>https://dev.to/elcapitan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F2648828%2F4d9e8133-29de-4164-b93c-8376ddd05528.png</url>
      <title>DEV Community: Ilean Monterrubio Jr</title>
      <link>https://dev.to/elcapitan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/elcapitan"/>
    <language>en</language>
    <item>
      <title>Building a Terminal Text Editor: The View (Part 3)</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Mon, 06 Apr 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/building-a-terminal-text-editor-the-view-part-3-12gd</link>
      <guid>https://dev.to/elcapitan/building-a-terminal-text-editor-the-view-part-3-12gd</guid>
      <description>&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://www.ilean.me/blog/building-a-terminal-text-editor-the-model-part-1/" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt;, I talked about the origin of this project, the early architectural decisions, and why I picked the GapBuffer with &lt;code&gt;IBuffer&lt;/code&gt; as the contract for all future buffer implementations. In &lt;a href="https://www.ilean.me/blog/building-a-terminal-text-editor-the-presenter-part-2/" rel="noopener noreferrer"&gt;Part 2&lt;/a&gt;, we got into the details of the Presenter, how it wires the components together, handles user input, manages state, and keeps the Model and View in sync. Now we can move on to the View.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the View?
&lt;/h2&gt;

&lt;p&gt;The View is the component that the user interacts with directly. All user inputs are collected and passed to the Presenter, the View doesn't decide what to do with them. It is also responsible for rendering the text the user types, along with any warnings, statistics like word count, and whether the file is unsaved. The View doesn't calculate any of this, it just displays whatever the Presenter sends it. Another example of how all three parts of the Model-View-Presenter separate concerns.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why FTXUI instead of ncurses
&lt;/h2&gt;

&lt;p&gt;In the proof of concept testing, I used ncurses, the de facto standard library for building terminal interfaces. It worked well for the initial testing, and I see a lot of projects still using it; it's tried and true. While ncurses would have been a great choice, I did a bit more research and found FTXUI, a modern C++ library with components to build interactive terminal user interfaces. I wanted to leverage that it uses modern C++, matching the project's early defined specification of targeting C++20.&lt;/p&gt;

&lt;h2&gt;
  
  
  The IView Interface
&lt;/h2&gt;

&lt;p&gt;While implementing the other components (Model and Presenter), I mocked up a thin interface for the View, &lt;code&gt;IView&lt;/code&gt;. At first, it was only taking character input and not accounting for special characters, and it didn't handle rendering any data. The proof of concept View just took the input, sent it to the Presenter, which would send it to the buffer and log it so I could see the data was actually flowing through the system.&lt;/p&gt;

&lt;p&gt;I was thinking of removing it, but after doing more research, I decided to expand the interface instead. Keeping &lt;code&gt;IView&lt;/code&gt; as an abstract class means that if someone wants to write their own View for an OS that isn't supported by FTXUI, they can implement the interface and plug it in without touching the Presenter or Model.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IView&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;public:&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;IView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;function&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;void&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;onInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ViewState&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;pair&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;getTerminalSize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;showMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isError&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A quick explanation, &lt;code&gt;run()&lt;/code&gt; starts the event loop and takes a callback for handling input, this is what the Presenter calls in its main loop. &lt;code&gt;render()&lt;/code&gt; takes the &lt;code&gt;ViewState&lt;/code&gt; we saw in Part 2 and draws it to the screen. &lt;code&gt;exit()&lt;/code&gt; and &lt;code&gt;getTerminalSize()&lt;/code&gt; do what they say, and &lt;code&gt;showMessage()&lt;/code&gt; is how the Presenter displays warnings like the "unsaved changes" prompt from the exit flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering the UI
&lt;/h2&gt;

&lt;p&gt;As we showed in Part 2, the &lt;code&gt;ViewState&lt;/code&gt; struct defines the contract for how the Presenter sends data to the View:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;ViewState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Content&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;visibleText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Text currently visible in viewport&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;cursorPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Linear position of cursor in visibleText&lt;/span&gt;

    &lt;span class="c1"&gt;// Status bar information&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;wordCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Total word count (primary metric for writers)&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Current file name (or "Untitled")&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isDirty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Unsaved changes indicator&lt;/span&gt;

    &lt;span class="c1"&gt;// UI state&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;statusMessage&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Temporary status message (e.g., "Saved", "Error: ...")&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;showHelp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Whether to show help overlay&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are three major types of data in the &lt;code&gt;ViewState&lt;/code&gt; contract: &lt;strong&gt;Content&lt;/strong&gt; , which tells us the &lt;code&gt;cursorPosition&lt;/code&gt; and the &lt;code&gt;visibleText&lt;/code&gt;. &lt;strong&gt;Status bar information&lt;/strong&gt; : &lt;code&gt;wordCount&lt;/code&gt;, &lt;code&gt;filename&lt;/code&gt;, and &lt;code&gt;isDirty&lt;/code&gt; which indicates whether the file is unsaved. Finally, the &lt;strong&gt;UI state&lt;/strong&gt; : &lt;code&gt;statusMessage&lt;/code&gt; and &lt;code&gt;showHelp&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;One of the biggest challenges was how to deal with the cursor, how to move it, and be able to add more to the buffer when the cursor moves. One of the simplest solutions is to break the &lt;code&gt;visibleText&lt;/code&gt; into three components: beforeCursor, cursor, and afterCursor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Element&lt;/span&gt; &lt;span class="n"&gt;FtxuiView&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;renderEditor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visibleText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="kt"&gt;size_t&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;static_cast&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kt"&gt;size_t&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;currentState&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursorPosition&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// Character at cursor with cyan background&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;cursorChar&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cursorChar&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;bgcolor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Cyan&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;color&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Color&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Black&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;size&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;substr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pos&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;hbox&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;after&lt;/span&gt;&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The cursor is highlighted with a cyan background so the user can see where they are in the text. This challenge existed in both ncurses and FTXUI, neither library gives you a built-in cursor for a custom text editor. You have to simulate it yourself.&lt;/p&gt;

&lt;h2&gt;
  
  
  Translating Keyboard Input
&lt;/h2&gt;

&lt;p&gt;The View is also responsible for translating raw keyboard events into the &lt;code&gt;InputEvent&lt;/code&gt; structs that the Presenter understands. FTXUI has its own event system, so the View needs a translation layer between what FTXUI gives us and what our Presenter expects. The &lt;code&gt;translateEvent()&lt;/code&gt; method handles this mapping:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt; &lt;span class="n"&gt;FtxuiView&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;translateEvent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ArrowLeft&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ARROW_LEFT&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ArrowRight&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ARROW_RIGHT&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Backspace&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BACKSPACE&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Return&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;ENTER&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CtrlQ&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTRL_Q&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="n"&gt;ftxui&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Event&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CtrlS&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTRL_S&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="c1"&gt;// Printable character&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;is_character&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;InputEvent&lt;/span&gt; &lt;span class="n"&gt;ie&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CHARACTER&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
        &lt;span class="n"&gt;ie&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;()[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;ie&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;UNKNOWN&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The pattern is straightforward, each FTXUI event maps to one of our &lt;code&gt;InputEvent&lt;/code&gt; types. If it's a printable character, we extract the character value. If we don't recognize the event, it returns &lt;code&gt;UNKNOWN&lt;/code&gt; and gets ignored. This is the same translation layer that would need to be reimplemented if someone wrote a new View using a different library, another benefit of the &lt;code&gt;IView&lt;/code&gt; interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the View Can Do So Far
&lt;/h2&gt;

&lt;p&gt;The View works well for the current state of the project. It renders text as the user types, displays the status bar with the filename, word count, and unsaved changes indicator, captures all input events and routes them to the Presenter, and toggles the help overlay with F1. It is not perfect, newlines don't render yet and the arrow keys can't fully navigate up and down through the text. But for Phase 1, it does its job as the passive layer in the MVP pattern.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up the Series
&lt;/h2&gt;

&lt;p&gt;This wraps up the three-part series on building wordNebula's Phase 1. Across these posts, we went from the origin of the project and why I chose Model-View-Presenter, through the Model with the GapBuffer and IBuffer interface, the Presenter that orchestrates everything and manages state, and finally the View that renders it all to the terminal.&lt;/p&gt;

&lt;p&gt;Building each layer independently and connecting them through contracts like &lt;code&gt;IBuffer&lt;/code&gt; and &lt;code&gt;IView&lt;/code&gt; made the project manageable. I could work on one piece at a time without worrying about breaking the others. That separation is the whole point of MVP, and it paid off.&lt;/p&gt;

&lt;p&gt;Phase 1 is complete but far from finished. There's more to build, but for now I'm stepping away to focus on other projects. If you want to explore the code, check out the project on &lt;a href="https://github.com/ileanmjr88/wordNebula" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>terminal</category>
      <category>architecture</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building a Terminal Text Editor: The Presenter (Part 2)</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Sun, 22 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/building-a-terminal-text-editor-the-presenter-part-2-2h08</link>
      <guid>https://dev.to/elcapitan/building-a-terminal-text-editor-the-presenter-part-2-2h08</guid>
      <description>&lt;h2&gt;
  
  
  Recap
&lt;/h2&gt;

&lt;p&gt;In &lt;a href="https://www.ilean.me/blog/building-a-terminal-text-editor-the-model-part-1/" rel="noopener noreferrer"&gt;Part 1&lt;/a&gt;, I talked about the origin of this project and the early architectural decisions. After picking Model-View-Presenter, breaking up the project into Model, View, and Presenter felt like the natural path forward. We tackled the &lt;code&gt;GapBuffer&lt;/code&gt; with &lt;code&gt;IBuffer&lt;/code&gt; as the contract for all future buffer implementations. With the Model standing on its own, we can move on to the Presenter.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is the Presenter?
&lt;/h2&gt;

&lt;p&gt;We already hinted in Part 1 that the Presenter is the orchestrator — the one that handles all the application's "business" logic. It is responsible for keeping both the Model and the View up to date. The Presenter takes user input from the View and passes it to the Model, then pulls the updated data from the Model and sends it back to the View for rendering. All communication between the two goes through the Presenter.&lt;/p&gt;

&lt;p&gt;Let's take the most common use case of a user pressing a single key stroke. In reality, they will be typing several in succession, and this process gets repeated constantly in the current implementation.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;From&lt;/th&gt;
&lt;th&gt;To&lt;/th&gt;
&lt;th&gt;What Happens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;User (actor)&lt;/td&gt;
&lt;td&gt;View&lt;/td&gt;
&lt;td&gt;Presses key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;View&lt;/td&gt;
&lt;td&gt;Presenter&lt;/td&gt;
&lt;td&gt;Passes the key event&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;Presenter&lt;/td&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;Calls insertChar()&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;Presenter&lt;/td&gt;
&lt;td&gt;Model&lt;/td&gt;
&lt;td&gt;Requests updated text&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;Presenter&lt;/td&gt;
&lt;td&gt;View&lt;/td&gt;
&lt;td&gt;Sends text to render&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;We see the big role that the Presenter fills and we will explore how we created contracts between the components to ensure that data is passed correctly. For wordNebula, this also means the Presenter will be where future logic lives.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wiring It Together
&lt;/h2&gt;

&lt;p&gt;Since this project is using modern C++ I wanted to take advantage of smart pointers and leverage Resource Acquisition Is Initialization (RAII). This way we don't have to explicitly manage memory — this is standard practice in modern C++ for any systems project.&lt;/p&gt;

&lt;p&gt;When starting to wire up the different components, including the mock View from the original testing I did before tackling the full project, I had to make some decisions on ownership. Here is how the three components get created in &lt;code&gt;main&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;presenter&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_shared&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WNebulaPresenter&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_shared&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WNebulaModel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;make_shared&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;FtxuiView&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="n"&gt;presenter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;setup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;presenter&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;run&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All three are created as &lt;code&gt;shared_ptr&lt;/code&gt;, but inside the Presenter they are stored differently:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;weak_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;IView&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;shared_ptr&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;WNebulaModel&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Model stays as a &lt;code&gt;shared_ptr&lt;/code&gt; because the Presenter needs to own it — it is the core data and must exist as long as the application is running. The View is stored as a &lt;code&gt;weak_ptr&lt;/code&gt; to break a circular dependency. The Presenter needs the View to push updates, and the View needs the Presenter to send input. If both held &lt;code&gt;shared_ptr&lt;/code&gt; to each other, neither would ever get cleaned up.&lt;/p&gt;

&lt;p&gt;The trade-off with &lt;code&gt;weak_ptr&lt;/code&gt; is that every time the Presenter needs to update the View, it has to convert it back to a &lt;code&gt;shared_ptr&lt;/code&gt; temporarily by calling &lt;code&gt;lock()&lt;/code&gt;. If the View still exists, &lt;code&gt;lock()&lt;/code&gt; gives us a valid &lt;code&gt;shared_ptr&lt;/code&gt; to work with. If it doesn't, we know the View is gone and can handle it gracefully. This happens inside &lt;code&gt;updateView()&lt;/code&gt; every time the Presenter needs to render new data to the screen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling User Input
&lt;/h2&gt;

&lt;p&gt;In the exploratory testing, I only worried about character user input — it was just to test if I understood the architecture. For the real implementation, I needed to actually account for special keys: &lt;code&gt;CTRL&lt;/code&gt;, &lt;code&gt;BACKSPACE&lt;/code&gt;, &lt;code&gt;DELETE&lt;/code&gt;, &lt;code&gt;ARROW KEYS&lt;/code&gt;, &lt;code&gt;ENTER&lt;/code&gt;, and so on.&lt;/p&gt;

&lt;p&gt;To handle this cleanly, I created an &lt;code&gt;InputEvent&lt;/code&gt; struct that translates raw keyboard input into semantic events:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;InputEvent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;enum&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;CHARACTER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ARROW_LEFT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ARROW_RIGHT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ARROW_UP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ARROW_DOWN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CTRL_LEFT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CTRL_RIGHT&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CTRL_UP&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CTRL_DOWN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;HOME&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;END&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;BACKSPACE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;DELETE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;ENTER&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;CTRL_S&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;CTRL_Q&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;ESCAPE&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// ... other types&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="n"&gt;Type&lt;/span&gt; &lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;character&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sc"&gt;'\0'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// Only valid for Type::CHARACTER&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This way the Presenter doesn't care about terminal escape codes — it just receives an event type and acts on it. The &lt;code&gt;handleInput&lt;/code&gt; method routes each event to the right operation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;WNebulaPresenter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;handleInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;type&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CHARACTER&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;onInsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;character&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;BACKSPACE&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;onDelete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTRL_LEFT&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;onCtrlLeft&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="n"&gt;InputEvent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;Type&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;CTRL_Q&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;onExit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="c1"&gt;// ... other cases&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Each operation follows the same pattern — delegate to the Model, mark dirty if needed, update the View:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;WNebulaPresenter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;onInsert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;insertChar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;isDirty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;updateView&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Viewport Management
&lt;/h2&gt;

&lt;p&gt;One of the things I learned along the way, or had to really think about, was the viewport. So far, the initial testing was me writing simple sentences, not full blog entries or longer format text. The area of the terminal is finite, so determining what portion of the buffer to display and how to present it was a challenge.&lt;/p&gt;

&lt;p&gt;The Presenter solves this by building a &lt;code&gt;ViewState&lt;/code&gt; — a struct that packages everything the View needs to render:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="nc"&gt;ViewState&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;visibleText&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;cursorPosition&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;wordCount&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;filename&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;isDirty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="n"&gt;showHelp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every time the Presenter updates the View, it pulls the current text and cursor position from the Model and sends it as a &lt;code&gt;ViewState&lt;/code&gt;. The View doesn't know anything about the buffer — it just renders whatever state it receives. Here is the &lt;code&gt;updateView()&lt;/code&gt; method that builds and sends that state:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;WNebulaPresenter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;updateView&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;ViewState&lt;/span&gt; &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;{};&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;visibleText&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;cursorPosition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getCursorPosition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;wordCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;getWordCount&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;filename&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;currentFilePath&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;empty&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="s"&gt;"Untitled"&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;currentFilePath&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;isDirty&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;isDirty&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;showHelp&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;showHelp&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;state&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice the &lt;code&gt;view.lock()&lt;/code&gt; — this is the &lt;code&gt;weak_ptr&lt;/code&gt; conversion we talked about in the Wiring section. Every time the Presenter needs to update the View, it checks that the View still exists before sending data.&lt;/p&gt;

&lt;h2&gt;
  
  
  State Management
&lt;/h2&gt;

&lt;p&gt;For managing state in this iteration of the project I kept it simple. The Presenter tracks a few boolean flags — &lt;code&gt;isDirty&lt;/code&gt; to know when the file hasn't been saved, &lt;code&gt;isRunning&lt;/code&gt; to control the main loop, &lt;code&gt;showHelp&lt;/code&gt; to toggle the help overlay, and &lt;code&gt;exitWarningShown&lt;/code&gt; for the quit confirmation.&lt;/p&gt;

&lt;p&gt;The most interesting one is the exit flow. If you have unsaved changes and press Ctrl+Q, the Presenter warns you and makes you press it again to actually quit:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;WNebulaPresenter&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;onExit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;isDirty&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="n"&gt;exitWarningShown&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;exitWarningShown&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;showMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Unsaved changes! Press Ctrl+Q again to quit."&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;isRunning&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;auto&lt;/span&gt; &lt;span class="n"&gt;v&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lock&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;v&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;exit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  What the Presenter Can Do So Far
&lt;/h2&gt;

&lt;p&gt;At this stage, the Presenter takes in all input events and determines if it is a special key or character, fully manages the state of the application and document, and keeps the Model and View in sync. It is the glue between the two, and because of MVP, it can be tested independently.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;In Part 3, I will talk about why I picked FTXUI instead of ncurses and what it offers out of the box that made the View layer easier to build. You can check out the project on &lt;a href="https://github.com/ileanmjr88/wordNebula" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>terminal</category>
      <category>architecture</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building a Terminal Text Editor: The Model (Part 1)</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Sat, 07 Mar 2026 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/building-a-terminal-text-editor-the-model-part-1-gca</link>
      <guid>https://dev.to/elcapitan/building-a-terminal-text-editor-the-model-part-1-gca</guid>
      <description>&lt;h2&gt;
  
  
  The Idea
&lt;/h2&gt;

&lt;p&gt;The idea for wordNebula came from seeing an interview with George R.R. Martin, where he talks about how he writes his books on a really old computer because his preferred word processor can't run on anything new. After finding out that the program is called WordStar, and some minor Googling, I saw it was entirely terminal-based, and thought that idea was novel.&lt;/p&gt;

&lt;p&gt;Growing up in the 90s, I went to an elementary school that gave us weekly computer lab time. I got the chance to use 5.25" floppy disks to play text-based games, and later learned to use the Macintosh computers and play the new versions of The Oregon Trail. So I can see how a writer can feel nostalgic and want to keep using a tool — if it's not broken, why change it?&lt;/p&gt;

&lt;p&gt;I am also a software engineer who has used Vim and Neovim, and I see why they have such great appeal. Those tools keep improving and staying up to date, and they are great at being distraction-free, keeping the user's focus on the task at hand.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Build Another One?
&lt;/h2&gt;

&lt;p&gt;Starting this project, I did a bit of research. Terminal word processors already exist. The open-source one I found is called &lt;a href="https://github.com/davidgiven/wordgrinder" rel="noopener noreferrer"&gt;WordGrinder&lt;/a&gt; — it has been around for a while and is written in C and Lua. It is a great project; check it out if you have the time. While terminal word processors are no longer popular, many writers still use them for the limited distraction and focus they offer.&lt;/p&gt;

&lt;p&gt;Honestly, I just thought it was something cool to build, and I just wanted something to work on for me. The overall stretch goal is to make wordNebula a full writer's tool — it would format the output for the user based on a config file or similar setup. The idea is that playwrights and screenwriters could fully write without any distraction or worrying about formatting, and novelists could structure their work by chapter. But that is the future; for now, it is a portfolio project that lets me explore terminal UI development, data structures, and architecture patterns in C++.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choosing an Architecture
&lt;/h2&gt;

&lt;p&gt;For this project, I decided early on to use modern C++ targeting C++20. Before diving into implementation, my first architectural decision was choosing a pattern: Model-View-Controller (MVC), Model-View-Presenter (MVP), or the newer Model-View-ViewModel (MVVM).&lt;/p&gt;

&lt;p&gt;Here is a quick comparison:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Aspect&lt;/th&gt;
&lt;th&gt;Model-View-Controller&lt;/th&gt;
&lt;th&gt;Model-View-Presenter&lt;/th&gt;
&lt;th&gt;Model-View-ViewModel&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Mediator&lt;/td&gt;
&lt;td&gt;Controller&lt;/td&gt;
&lt;td&gt;Presenter&lt;/td&gt;
&lt;td&gt;ViewModel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;View Role&lt;/td&gt;
&lt;td&gt;Active, interacts with Model directly&lt;/td&gt;
&lt;td&gt;Passive, passes data to Presenter&lt;/td&gt;
&lt;td&gt;Passive, data is bound to ViewModel&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Data Binding&lt;/td&gt;
&lt;td&gt;Manual updates&lt;/td&gt;
&lt;td&gt;Manual, Presenter sends to Model&lt;/td&gt;
&lt;td&gt;Automatic via data binding&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Testability&lt;/td&gt;
&lt;td&gt;Low&lt;/td&gt;
&lt;td&gt;Good&lt;/td&gt;
&lt;td&gt;Best&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Separation&lt;/td&gt;
&lt;td&gt;Basic&lt;/td&gt;
&lt;td&gt;Better&lt;/td&gt;
&lt;td&gt;Best&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Best For&lt;/td&gt;
&lt;td&gt;Small projects&lt;/td&gt;
&lt;td&gt;Medium to complex&lt;/td&gt;
&lt;td&gt;Large, data-driven UIs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;After researching these patterns and studying block diagrams, I picked Model-View-Presenter. The main reason is that it completely separates all three concerns:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Model&lt;/strong&gt; is the text buffer. Its only job is maintaining the buffer — storing text, tracking the cursor, and supporting insertions and deletions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The View&lt;/strong&gt; handles the UI layout, how the data is displayed, and capturing the user's key inputs, which it passes to the Presenter.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Presenter&lt;/strong&gt; is the orchestrator. It receives input from the View, passes text operations to the Model, then requests the updated data from the Model to refresh the View.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All communication flows through the Presenter. This is the main reason the architecture is testable — and while we all know unit tests aren't fun, we understand why they're important.&lt;/p&gt;

&lt;h2&gt;
  
  
  Getting My Hands Dirty
&lt;/h2&gt;

&lt;p&gt;With the architecture decision made and the clean separation of concerns giving me the ability to work on different parts of the project independently, I started with a simple proof of concept. I implemented a basic Model text buffer that accepts keystrokes and prints to a log file. It wasn't perfect, but it got me to understand the basic structure and play a bit with MVP before committing to the full implementation.&lt;/p&gt;

&lt;p&gt;For this I set up what I called a TextBuffer, which was just a string where I added things to it via a simple Presenter and View with ncurses. I had Copilot help me test it, and what I learned was that I should really start the project by implementing the Model first — it is such a critical part of the project, and it prompted me to do more research on how word processors handle their buffers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Model: Choosing a Data Structure
&lt;/h2&gt;

&lt;p&gt;I started doing research on text buffers and saw some really appealing ones. One was the Gap Buffer, which I believe Emacs uses. Another was the Piece Table, used by Microsoft Word, and I found a blog from the VS Code team about their usage of it and their findings: &lt;a href="https://code.visualstudio.com/blogs/2018/03/23/text-buffer-reimplementation" rel="noopener noreferrer"&gt;Text Buffer Reimplementation&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After observing this and understanding the differences between them, I picked the Gap Buffer for the initial implementation. This decision became clear once I started to define the use cases — since this is a terminal-based UI, users probably won't be highlighting and removing large chunks of text, so undo and redo can be implemented much later. And also a key difference from any modern code editor: we don't need multiple cursors, only a single one where the user can add to the document.&lt;/p&gt;

&lt;p&gt;The idea behind a Gap Buffer is straightforward. You have an array with a "gap" of empty space that follows the cursor around. When you type, you drop characters into the gap. When you move the cursor, the gap moves with it. Here is what that looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Before: [H][e][l][l][o][___GAP___][W][o][r][l][d]
                        ^ cursor

Insert ',':
After: [H][e][l][l][o][,][__GAP__][W][o][r][l][d]
                           ^ cursor

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The core of the insertion is simple — move the gap to the cursor, make room if needed, and drop the character in:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;GapBuffer&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;insertChar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;moveGapToCursor&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;getGapSize&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;expandGap&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="n"&gt;buffer&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;gapStart&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;gapStart&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="n"&gt;cursor&lt;/span&gt;&lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It was also key to use interface abstract classes to make sure I could update the buffer if I ever make a new implementation. This was very important to me because moving into the future we might need flexibility — being able to add undo/redo, or add styling. I was so unsure early on that I thought having that flexibility was very important to have.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;IBuffer&lt;/code&gt; interface defines the full contract that any buffer must fulfill. Here is a trimmed version showing the method signatures:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight cpp"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;IBuffer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;public:&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="o"&gt;~&lt;/span&gt;&lt;span class="n"&gt;IBuffer&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Text Operations&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;insertChar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;insertText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;deleteChar&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;deleteForward&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Text Access&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;getText&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="n"&gt;std&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;string&lt;/span&gt; &lt;span class="n"&gt;getTextRange&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;start&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;length&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;getLength&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Cursor Management&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;getCursorPosition&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;setCursorPosition&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;position&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="n"&gt;moveCursor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;offset&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Smart Navigation&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;findNextWordBoundary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fromPos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;findPrevWordBoundary&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fromPos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;findNextParagraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fromPos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;findPrevParagraph&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;fromPos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Statistics&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;getWordCount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;virtual&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="n"&gt;getParagraphCount&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="k"&gt;const&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With this interface, if I ever need to swap in a Piece Table for advanced undo/redo support down the road, I can do it without rewriting the Presenter or View. The full interface with detailed Doxygen documentation is in the &lt;a href="https://github.com/ileanmjr88/wordNebula" rel="noopener noreferrer"&gt;repository&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Model Can Do So Far
&lt;/h2&gt;

&lt;p&gt;The Model can fully add text at the cursor position, and the gap can grow dynamically. It supports full cursor navigation, text insertion and deletion, word count, and paragraph count. It also has a comprehensive test suite with Google Test. The Model stands on its own — it was built and tested before the Presenter or View ever existed.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;In Part 2 I will go over the Presenter, which takes the Model and orchestrates everything — coordinating between the buffer and the UI, and managing application state. The Presenter and View are already implemented; you can check out the project on &lt;a href="https://github.com/ileanmjr88/wordNebula" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>cpp</category>
      <category>terminal</category>
      <category>architecture</category>
      <category>programming</category>
    </item>
    <item>
      <title>I2C Driver and Simulation</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Mon, 03 Feb 2025 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/i2c-driver-and-simulation-3mcc</link>
      <guid>https://dev.to/elcapitan/i2c-driver-and-simulation-3mcc</guid>
      <description>&lt;p&gt;Throughout my career, I have worked extensively with microcontrollers and systems on chips (SoCs). These devices often need to communicate with peripherals using protocols such as UART, I2C, SPI, and 1-Wire (W1). These are short-range communication protocols primarily used for interfacing with peripheral devices.&lt;/p&gt;

&lt;p&gt;Common peripherals in microcontroller-based projects include real-time clocks (RTC), digital-to-analog converters (DACs), and analog-to-digital converters (ADCs), among others. Whether you're a hobbyist or a professional engineer, mastering these communication protocols is essential for successfully working with embedded systems.&lt;/p&gt;

&lt;p&gt;In this blog entry, I will focus on the I2C protocol and how to emulate its read and write functions. I2C is a widely used two-wire protocol that allows multiple devices to communicate over the same bus. Each I2C device has an address, which is either predefined by the manufacturer or, in some cases, configurable by the user or system architect.&lt;/p&gt;

&lt;p&gt;When a microcontroller or microprocessor needs to read from or write to an I2C device, it sends the device’s address along with a read or write bit. It then specifies the registers address range it wants to access. Understanding this process is fundamental to effectively integrating I2C peripherals into embedded systems.&lt;/p&gt;

&lt;h2&gt;
  
  
  Description of I2C Sequence
&lt;/h2&gt;

&lt;p&gt;To understand how I2C transmits data, we’ll focus on the logical sequence rather than the physical wiring aspects.&lt;/p&gt;

&lt;p&gt;I2C communication begins with a &lt;strong&gt;Start condition&lt;/strong&gt; , signaling to all connected devices that the master intends to communicate. This is followed by the &lt;strong&gt;Address frame&lt;/strong&gt; , which can be either a 7-bit or 10-bit address, depending on the device specifications. Each device on the bus checks the incoming address and only responds if it matches its own; all other devices ignore the transmission.&lt;/p&gt;

&lt;p&gt;Next, the &lt;strong&gt;Read/Write (R/W) bit&lt;/strong&gt; is sent. This bit determines whether the master intends to &lt;strong&gt;write&lt;/strong&gt; (0) or &lt;strong&gt;read&lt;/strong&gt; (1) from the addressed device. The addressed device then responds with an &lt;strong&gt;ACK (Acknowledge) or NACK (Not Acknowledge)&lt;/strong&gt; bit to confirm whether it has successfully recognized the request.&lt;/p&gt;

&lt;p&gt;Once acknowledged, data transfer begins in &lt;strong&gt;8-bit (1-byte) sequences&lt;/strong&gt;. After each byte, the receiving device sends another &lt;strong&gt;ACK/NACK bit&lt;/strong&gt; to indicate whether it successfully received the data. This process continues until all required data has been transmitted.&lt;/p&gt;

&lt;p&gt;Finally, the communication ends with a &lt;strong&gt;Stop condition&lt;/strong&gt; , signaling that the bus is now free for other devices.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;I2C Data Transfer Sequence&lt;/strong&gt;
&lt;/h4&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start Condition&lt;/strong&gt; – Signals the beginning of communication&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Address Frame&lt;/strong&gt; – Specifies the target device (7-bit or 10-bit)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read/Write (R/W) Bit&lt;/strong&gt; – Defines the direction of data transfer (0 = Write, 1 = Read)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ACK/NACK Bit&lt;/strong&gt; – Confirms receipt of the address by the device&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;8-bit Data Transmission&lt;/strong&gt; – Data is sent in 8-bit sequences&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ACK/NACK Bit&lt;/strong&gt; – Acknowledges each byte transfer&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stop Condition&lt;/strong&gt; – Ends the communication session&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;By following this structured approach, I2C enables efficient multi-device communication using just two wires.&lt;/p&gt;

&lt;h2&gt;
  
  
  Emulation of I2C
&lt;/h2&gt;

&lt;p&gt;For this emulation, we will focus on reading and writing a single byte of data while covering the key components of the I2C protocol: the &lt;strong&gt;Address Frame, Read/Write Bit, and 8-bit Data Transmission&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In our approach, we will perform two &lt;strong&gt;8-bit data transmissions&lt;/strong&gt; :&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Register Address&lt;/strong&gt; – Specifies which register we want to access within the device.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Data&lt;/strong&gt; – The actual data being written or read from the specified register.&lt;/li&gt;
&lt;/ol&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Understanding Registers&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;If you're unfamiliar with registers, think of them as small, high-speed storage locations within hardware. They function similarly to RAM but are much faster and operate at a lower level. Registers are essential for configuring and controlling hardware devices.&lt;/p&gt;

&lt;p&gt;Each I2C device has predefined register addresses, which are typically found in the device’s &lt;strong&gt;datasheet&lt;/strong&gt;. However, for simplicity, our emulation will use a structured data representation instead of referencing a real datasheet. The goal is to ensure we correctly read and write data within our emulated environment.&lt;/p&gt;

&lt;h4&gt;
  
  
  I2C Data Structure
&lt;/h4&gt;

&lt;p&gt;To emulate an I2C device, we will use a simple data structure that mimics the essential components of an I2C transaction. This structure will allow us to simulate reading from and writing to an I2C device.&lt;/p&gt;

&lt;h4&gt;
  
  
  &lt;strong&gt;Data Structure&lt;/strong&gt;
&lt;/h4&gt;

&lt;p&gt;The following structure represents the key elements involved in I2C communication:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;uint8_t: device_address&lt;/li&gt;
&lt;li&gt;uint8_t: register_address&lt;/li&gt;
&lt;li&gt;uint8_t: data&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Write Sequence
&lt;/h3&gt;

&lt;p&gt;To simulate writing data to an I2C device, we follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add the Write Bit to the Address&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The device address is bitwise shifted, and the &lt;strong&gt;write bit (0)&lt;/strong&gt; is appended.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Initialize Local Variables&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Prepare the payload, including the device address and register to write to.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set Up the Emulated I2C Data Structure&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Assign the device address and register address.&lt;/li&gt;
&lt;li&gt;Clear previous data before writing new data.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mock the Hardware Write&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Since each embedded platform has unique I2C functions, we emulate this by verifying the address in our data structure and writing the data accordingly.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Read Sequence
&lt;/h3&gt;

&lt;p&gt;To simulate reading data from an I2C device, we follow these steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Add the Read Bit to the Address&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;The device address is bitwise shifted, and the &lt;strong&gt;read bit (1)&lt;/strong&gt; is appended.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Initialize Local Variables&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Prepare the payload and send the read request to the emulated device.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Set Up the Emulated I2C Data Structure&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Assign the device address and register address.&lt;/li&gt;
&lt;li&gt;Ensure previous data is cleared before reading new data.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mock the Hardware Write&lt;/strong&gt; &lt;em&gt;(For Register Selection)&lt;/em&gt;

&lt;ul&gt;
&lt;li&gt;Before reading, an I2C device typically expects a write operation to set the register address.&lt;/li&gt;
&lt;li&gt;This step emulates sending the register address before reading data.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mock the Hardware Read&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Read the data from the structure and verify that it matches the expected value.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Closing Note
&lt;/h2&gt;

&lt;p&gt;This exercise is a great way to deepen your understanding of the I2C protocol and demonstrate your knowledge practically.&lt;/p&gt;

&lt;p&gt;You can find my implementation on my &lt;a href="https://github.com/ileanmjr88/daft-embedded" rel="noopener noreferrer"&gt;&lt;strong&gt;GitHub&lt;/strong&gt;&lt;/a&gt; account. The repository includes a &lt;strong&gt;development container (dev container)&lt;/strong&gt; for easy setup, allowing you to run the project seamlessly in a pre-configured environment. Additionally, I've provided a &lt;strong&gt;CMakeLists.txt&lt;/strong&gt; file, ensuring cross-platform compatibility while keeping the implementation purely in &lt;strong&gt;C&lt;/strong&gt; for maximum portability.&lt;/p&gt;

&lt;p&gt;Feel free to explore, run, and modify the code! 🚀&lt;/p&gt;

</description>
      <category>embedded</category>
      <category>i2c</category>
      <category>microcontrollers</category>
    </item>
    <item>
      <title>My First Hackathon Experience: A Journey Back to the 90s</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Tue, 24 Dec 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/my-first-hackathon-experience-a-journey-back-to-the-90s-5cep</link>
      <guid>https://dev.to/elcapitan/my-first-hackathon-experience-a-journey-back-to-the-90s-5cep</guid>
      <description>&lt;p&gt;I had never participated in a hackathon before this year, but thanks to Eric from Houston Code and Coffee, who encouraged me to step out of my comfort zone, I decided to give it a shot. The event was hosted by &lt;strong&gt;Codédex&lt;/strong&gt; , and the theme couldn’t have been more nostalgic: building a website inspired by the 90s.&lt;/p&gt;

&lt;p&gt;As someone who grew up in the 90s and vividly remembers spending time on elementary school computers, and surfing the web during the early days of the internet, this theme struck a personal chord with me. It felt like a perfect opportunity to relive those simpler times while flexing my creative muscles.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Choosing the Track: Reimagining My Personal Website&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;The hackathon offered two tracks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Reimagine a Company Website:&lt;/strong&gt; Adding a 90s twist to a modern company’s website.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Create a Personal Website:&lt;/strong&gt; Designing a website that reflects your personality with a 90s vibe.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I opted for the personal track, deciding to revamp a portion of my current professional website. To prepare, I dove into a rabbit hole of nostalgia, exploring personal websites preserved by the &lt;strong&gt;Wayback Machine&lt;/strong&gt; and web archives. It was fascinating to see how people expressed their creativity in the era of low resolutions, tiled backgrounds, and vibrant animations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Design Decisions: A Tribute to 90s Aesthetics&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Here are some of the elements I incorporated into my design:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tiled Backgrounds:&lt;/strong&gt; I used a repeating star pattern to capture the classic 90s look.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Neon and Clashing Colors:&lt;/strong&gt; These were essential to evoke the chaotic yet charming visual style of the decade.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GIF Animations:&lt;/strong&gt; Scaled-up, pixelated animations added movement and energy to the site.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fixed Resolution:&lt;/strong&gt; I emulated a resolution of 800x600 pixels so that the site scaled up and maintained its fixed proportions.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The process of building the website took me back to my middle school days when I first learned HTML and CSS in our computer lab. It reminded me of the joy of experimenting with simple yet effective designs. Although I wouldn’t call myself the most imaginative web designer, this hackathon pushed me to think outside the box and experiment with ideas I hadn’t considered before.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dancing-beijinho-5fad4d.netlify.app/" rel="noopener noreferrer"&gt;Live Demo&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Observations and Shoutouts&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;I want to give a big shoutout to the &lt;strong&gt;Codédex&lt;/strong&gt; team for organizing such an inclusive and beginner-friendly event. They went above and beyond to ensure participants of all skill levels felt welcome. Here’s what stood out:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Comprehensive Workshops:&lt;/strong&gt; They offered live crash courses in HTML, CSS, JavaScript, and web design.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Helpful Resources:&lt;/strong&gt; From asset recommendations to hosting service tips, they equipped us with everything we needed to succeed.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community Spirit:&lt;/strong&gt; The opening and closing ceremonies fostered a sense of belonging and celebration.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Overall, the hackathon was a fantastic experience. It allowed me to learn, create, and connect with like-minded individuals while indulging in a wave of nostalgia. I’m excited to participate in more hackathons in the future and continue exploring new ideas.&lt;/p&gt;

</description>
      <category>astrojs</category>
      <category>90swebsites</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Finding Community in Houston’s Tech Scene</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Thu, 31 Oct 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/finding-community-in-houstons-tech-scene-6ge</link>
      <guid>https://dev.to/elcapitan/finding-community-in-houstons-tech-scene-6ge</guid>
      <description>&lt;p&gt;Since my layoff in early September, I've been navigating the tech landscape in Houston and documenting my observations along the way. Surprisingly, Houston offers much more in technology than I initially expected. While it’s known globally as the Oil &amp;amp; Gas capital, Houston has a growing tech scene where companies and events span multiple industries, not just energy.&lt;/p&gt;

&lt;p&gt;In my search, I found the Ion District, a collaborative tech hub that hosts various events almost daily. To get started with networking, I’ve attended a few groups, mostly at the Ion and the Improving offices—a software development and IT consultancy with an inviting space for meetups and community events.&lt;/p&gt;

&lt;p&gt;In my effort to start networking, I have attended three different groups. Two are hosted at the Ion, and the other is at the Improving offices. Improving is a software development and IT consultant company hosting several Meetup groups related to software. They have a dedicated room for these events with free drinks and snacks. This is a step in the right direction to foster innovation and, more importantly, have the innovation stay in Houston.&lt;/p&gt;

&lt;h2&gt;
  
  
  Events
&lt;/h2&gt;

&lt;p&gt;Given my background in Electrical Engineering and Computer Science, the groups I have attended reflect areas of interest and things I have done in my career. The events are very inclusive, which is excellent. It shows how diverse Houston is and that it accepts everyone as they are.&lt;/p&gt;

&lt;h3&gt;
  
  
  Code &amp;amp; Coffee
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://codeandcoffee.org/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.meetup.com/houston-code-and-coffee/" rel="noopener noreferrer"&gt;Meetup&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Held: Monthly on Sunday&lt;/p&gt;

&lt;p&gt;This is the first group I attended. I can be social, but getting out of my comfort zone takes effort. I did get out of my comfort because the first meeting was about 90% first-time attendees. The meetings usually start with everyone signing in and getting a name tag and some time for mingling. Once Eric, the host, gives a short introduction along with the Improving host, which offers a quick rundown about the company (which, in reality, is about a minute, which is brief, in my opinion). The introduction ends with a group circle where we introduce ourselves to each other. After that, it is more mingling; you can work on a project together if someone has one.&lt;/p&gt;

&lt;p&gt;This group is very inclusive, both in its diversity and in welcoming new learners and seasoned veterans. There is even a professor who attends and sets up an area of the room where learners can ask questions and work through it together. So bring your computer if that is the case.&lt;/p&gt;

&lt;h3&gt;
  
  
  Houston Robotics Group
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://houstonroboticsgroup.com/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;a href="https://www.meetup.com/houstonroboticsgroup/" rel="noopener noreferrer"&gt;Meetup&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Held: Weekly on Saturday&lt;/p&gt;

&lt;p&gt;This group is more of a working session, and people come to work on robotics projects. You walk in and meet as many people as you like. If you still need to get a robot project, no pro; the event organizers lend you a kit and set up the beginners to learn to assemble it. They also lend you an Arduino and show beginners how to code the LED blinking light. All levels of robotic enthusiasts are welcome. While yes, there are season roboticists, most of the attendees are University students working on improving skills and knowledge who are happy to help.&lt;/p&gt;

&lt;p&gt;Typically, the attendees pick a restaurant for dinner after the meeting, a short ride on the Metro Rail.&lt;/p&gt;

&lt;h3&gt;
  
  
  Cup of Joey
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://www.cupofjoey.org/" rel="noopener noreferrer"&gt;Website&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Held: Weekly on Friday&lt;/p&gt;

&lt;p&gt;This is the only general or business networking event I have attended. You sign in and get a name sticker, but you do not write down your name; you write down your purpose in life. Recommendation: Have something in mind before attending. I did not know and was thrown off for a few minutes while I thought of something. Overall, it is just a networking event. Having your purpose on the name tag makes it easier to strike up conversations. You will see the diversity of Houston in this group the most, from the different industries and people of all ages connecting and finding discussions.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ending Notes
&lt;/h2&gt;

&lt;p&gt;While these are the few I picked to attend, there are more software meet-ups in Houston. Most might be in the Improving offices, and you can find them via the Meetup app.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.meetup.com/react-htx/" rel="noopener noreferrer"&gt;React HTX&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.meetup.com/python-14/" rel="noopener noreferrer"&gt;PyHou - Houston Python Enthusiast&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.meetup.com/meetup-group-zwgkdivg/" rel="noopener noreferrer"&gt;Houston Hackers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.meetup.com/Houston-Machine-Learning/" rel="noopener noreferrer"&gt;Houston Machine Learning&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://gdg.community.dev/gdg-houston/" rel="noopener noreferrer"&gt;Google Developer Group Houston&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://lu.ma/hou-hardware-meetup" rel="noopener noreferrer"&gt;Houston Hardware Meetup&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.meetup.com/side-project-society/" rel="noopener noreferrer"&gt;Side Project Society&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://iondistrict.com/" rel="noopener noreferrer"&gt;Ion District&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those are the ones I have seen; I am sure more exist. Just doing a deep search on apps like Meetup, Eventbrite, and others will show how many more there are.&lt;/p&gt;

</description>
      <category>houstontech</category>
      <category>techcommunity</category>
      <category>houstontx</category>
      <category>networking</category>
    </item>
    <item>
      <title>Beyond the Blinky LED: Building a Command Interpreter for Microcontrollers</title>
      <dc:creator>Ilean Monterrubio Jr</dc:creator>
      <pubDate>Thu, 31 Oct 2024 00:00:00 +0000</pubDate>
      <link>https://dev.to/elcapitan/beyond-the-blinky-led-building-a-command-interpreter-for-microcontrollers-19dd</link>
      <guid>https://dev.to/elcapitan/beyond-the-blinky-led-building-a-command-interpreter-for-microcontrollers-19dd</guid>
      <description>&lt;p&gt;The blinky LED project is simple: you connect an LED to a digital output pin and add a resistor in line with either the positive or negative terminal. By adjusting the resistor, allows you to control the LED's brightness based on its color and voltage needs. This beginner project gives you a hands-on understanding of how code can control real-world objects.&lt;/p&gt;

&lt;p&gt;You completed this project and are now looking for the next one. The next step is to build a command interpreter.&lt;/p&gt;

&lt;p&gt;A command interpreter allows you to type commands into your microcontroller and get a response in real-time—just like the terminal on your computer. With this project, you'll build a simple command interpreter that lets you control your microcontroller interactively, a skill commonly used in embedded systems for testing and debugging.&lt;/p&gt;

&lt;h2&gt;
  
  
  What you will learn
&lt;/h2&gt;

&lt;p&gt;The reason this is an excellent next project is because you will need to learn and understand:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Serial Communication&lt;/strong&gt; : Transmitting data between your microcontroller and computer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Setting Baud Rate&lt;/strong&gt; : Defining the speed for data transmission.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Line Termination&lt;/strong&gt; : Ensuring that each command the microcontroller understands you type.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Working with Serial Emulators&lt;/strong&gt; : Testing commands directly from your computer.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;String Parsing and Comparison&lt;/strong&gt; : Break down commands and check them against defined instructions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;State Machine Programming&lt;/strong&gt; : Creating a structured way for your program to handle different commands logically.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Recommended commands
&lt;/h2&gt;

&lt;p&gt;Set a few commands in your microcontroller command interpreter.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;help&lt;/strong&gt; - Lists available commands. Example: &lt;strong&gt;help&lt;/strong&gt; displays all commands with short descriptions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;echo&lt;/strong&gt; - Repeats the text you type. Example: &lt;strong&gt;echo Hello&lt;/strong&gt; responds with "Hello."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;analog-read &lt;/strong&gt; - Reads an analog value from a specified channel. Example: &lt;strong&gt;analog-read 1&lt;/strong&gt; gives a reading from channel 1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;digital-on &lt;/strong&gt; - Turns a digital output ON. Example: &lt;strong&gt;digital-on 2&lt;/strong&gt; turns on channel 2.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;digital-off &lt;/strong&gt; - Turns a digital output OFF. Example: &lt;strong&gt;digital-off 2&lt;/strong&gt; turns off channel 2.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Wrap up
&lt;/h2&gt;

&lt;p&gt;This project is a solid introduction to building embedded command interpreters. As you set up each command, you’ll get a feel for reading analog inputs, controlling digital outputs, and managing serial communication. Even in more advanced systems, serial communication is a reliable and efficient method for testing, debugging, and even real-time control in industrial applications.&lt;/p&gt;

&lt;p&gt;With each command you add, you’re expanding your microcontroller’s capabilities. Dive in, experiment, and enjoy the process!&lt;/p&gt;

</description>
      <category>mcu</category>
      <category>2ndbegineerproject</category>
      <category>arduino</category>
      <category>rpi</category>
    </item>
  </channel>
</rss>
