Unity Typewriter and Windowing System

Made back in 2013. My first large experiment.
Windows can be dragged, zoomed, grouped,
typed into, saved and loaded.

Built in the Unity game development environment, and written in "UnityScript" (a language loosely based on JavaScript). I later ported it to C# in 2014 in order to have better support for polymorphism and subtyping.

My initial plan was to build a framework for AR and VR (Augmented Reality and Virtual Reality), but integrating further support for web, video, audio, and other media seemed like it was going to be a laborious task. So I decided to move on and build on top of platforms where I could easily work with existing files and code libraries, instead of writing each media utility from the ground up.

It was an incredibly fun project, and it was my first big trial by fire where I learned how to better manage and organize large complex projects.

In my first iteration, I had windows in the same plane (parallel to the screen, all the same distance away from the camera). This caused severe visual glitches whenever portions of two or more windows overlapped. I was new to Unity and game engines and 3D, and if there was a workaround for this, I was unable to discover it at the time. To remedy this, I ended up learning a lot about 3D vector math.

[~] note to self, add diagrams here.

I spaced windows away from the others, giving each its own plane in 3D. Then they could move around the user's screen without any anomalies nor visual chimera glitches. But placing windows further from the camera makes them smaller. So as windows are placed further away, they are proportionally resized to maintain the appearance that all of the windows were located in the same "Desktop" plane.

When I move back to working on 3D interfaces again, I may revisit this project, clean it with the better practices I now know, and upload a version of source code which can run on modern versions of Unity. But likely I will instead make a more elegant solution than this one. It was an incredible learning experience though.

click a section to expand

Rich Text Editor in Godot

My current project.

A subset of a larger system I'm working on in Godot.

Currently on hold. I'll be returning to this after I finish this website and secure a job.

More details, tutorials, and full code will be available in the Documentation section of this website when it's done.


Each letter is a node, and each line is a node containing zero or more letters.

Letters contain internal data for their foreground and background color, their font name, their font size, and their style.

The window itself currently has its graphical style hardcoded, but I plan to extend this (much later).

Clicking within the window will activate it, and place the text cursor at the mouse position. Clicking and dragging the mouse will select a region of text. (You can also select text by holding SHIFT and pressing: the arrow keys, HOME button, and the END button.)

When CTRL is held down before the user clicks the window, the window functions differently:

  • Holding CTRL and left-click dragging will move the window.
  • Holding CTRL and right-click dragging will resize the window.
    (The direction of the resize depends on the quadrant of the window clicked. If the user holds CTRL, right-clicks on the leftmost side of the window, then drags out, the rightmost side of the window will stay where it is, and the leftmost side will follow the mouse and expand or contract.)

Any action which changes the document is called through a COMMANDS class, which creates an Action object to track changes.

After an Action object is created, its Action.do() function is called, then the Action object is pushed onto an undo stack. When the user presses the hotkey for undo (CTRL+Z), the Action object is popped, its Action.undo() function is called, and the Action object is pushed onto the redo stack.

Linux Framebuffer Windowing System

Due to the Coronavirus pandemic, I don't presently have access to this code, but I will upload images of it when I am able.

Written in C, then ported to C++. Runs in a Linux terminal, and plots individual pixels to the screen. Keyboard input only.

Windows can be moved and resized. Windows contain lines and headers (which contain sub-sections of more headers and lines). Headers can be folded and expanded, allowing quick navigation between dense sets of hierarchical data without ever taking your hands off the keyboard.

In this app the screen is rendered from blocks, like a chess board. The screen's grid of blocks does not change location nor size, but behind it is a canvas (which can move around) filled with windows and lines and characters (which can also move around). Think of the screen's grid like a camera fixed in place while people move around within (or outside of) its view. Every time the program paints pixels to the screen, the screen's grid "camera" takes a snapshot of blocks it can currently see, and displays them at actual pixel coordinates on the computer's monitor.

The canvas can place windows anywhere, and can scroll to view windows placed off the screen. Windows can be moved and resized. Windows have a solid background color for each block they display within the grid system. Windows can additionally assign a default foreground color for text (different from the workspace's default font color). Windows contain a border of solid-colored grid elements, and a central document. The central document contains rows of lines. Lines can be one of two types: [1] a simple line of character objects to display and edit [2] or a header, which functions as a simple line, but additionally has a sub-document grouped under it. Headers can fold (i.e. collapse and hide their sub-document region, showing only their title) and unfold. Header titles can be edited just like any other line. Sub-documents under headers can contain lines of either type.

When the user selects a window, then activates it, the program shifts into line selection mode. Once the desired line is selected, the user hits [CONTROL+ENTER] to shift from line selection mode -> into editing mode. From here line characters can be typed, selected, copied, pasted, and deleted as if typing in a standard text editor. [CONTROL+ENTER] likewise shifts back from text editing mode -> back to line selection mode.

Lines store a linked-list of character objects. Each character object has: a foreground color, a background color, references to neighbors, and the symbol to display at its grid coordinate. (To specifically differentiate, let's define "symbol" in this context to mean: a letter, number, or any visible character which results from hitting a key on a keyboard.)

When the program draws to the screen, each grid element with a window will report what is located at each blocky grid coordinate. Borders for windows tell the grid to paint a single color at each pixel in that rectangle. Coordinates in a window which do contain a character object report their block's background color and which foreground color the grid should paint their symbol. When the character object tells the grid which symbol to draw, the grid looks up that symbol in the font object.

The font object is a series of 2D regions I programmed manually. Each region has elements containing 1's and 0's representing pixels in a rectangle. The 1's are the pixel positions in the rectangle to draw the symbol, and the 0's represent where to draw the background color. Each typeable character on the keyboard is mapped to one of the 2D regions in the font array (fun example: the 2D region mapped to the space bar is all 0's, resulting in an empty box; pure background color).

So the grid gets the foreground, the background, and the symbol data, then paints the pixels onto the portion of the screen associated with that grid coordinate's region.

This process is repeated for each window and each line on the screen until... viola! You have a screen with blocks and letters and colors.

Next up, how to use it.

  • Primary design rule for this application:

    You should never have to take your hands off the keyboard nor move them out of typing position (i.e. you'll never have to take your index fingers off the [f] and [j] keys).

    • This lets you scroll around and view other windows.
    • The [A] and [Q] keys adjust the speed at which the canvas scrolls.
    • These controls are only deactivated when in typing mode. Otherwise they work no matter where you are in selection mode.
    • The [IJKL] keys are for selection
      across different contexts.
    • The [P] and [;] keys change the scope of what is selected (from high up canvas, to low down character editing).
    • The [U] and [O] keys do a few different things at different levels, but it's pretty straightforward.
      • [IJKL] keys select windows.
        (The canvas will automatically scroll to view any selected window if off-screen).
      • The [;] key activates the selected window.
      • The [P] key exits an active window and returns the scope to choosing windows within the canvas.
      • The [U] key toggles "move the selected window" mode. Once on, the [IJKL] keys nudge the window (pretend these are the arrow keys). And pressing [U] again turns off move-mode.
      • The [O] key toggles "resize the selected window" mode. [IJKL] keys resize the window. [I] and [K] shrink and expand the window vertically. [J] and [L] shrink and expand the window horizontally. Pressing [O] again turns off resize-mode.
      • the [I] and [k] keys allow you to select lines of text
      • the [J] and [L] keys fold and unfold headers
        (if the current header is selected and folded then [J] will select and scroll up to the header's parent).
      • the [U] and [O] keys go to
        the next and previous header, respectively.
      • the ([) and ['] keys scroll the current window
        (similar to how the [ESDF] keys scroll the canvas)
      • the [P] key will deactivate the window and move selection up to the canvas level.
      • the [;] key or [CTRL+ENTER] combo will activate the selected line (or header) and the program shifts into text editing mode.
      • All keyboard inputs which normally type a character will insert a character at the highlighted position.
      • The [ENTER] key will make a new line and move the highlighted position to it. The window is scrolled down one line if the user presses [ENTER] at the bottom of the window's viewable area.
      • [BACKSPACE], [DELETE], [HOME], [END], [PAGEUP], [PAGEDOWN], and the up down left right [ARROW_KEYS] function as they do in regular text editors, such as Notepad.
      • The shift key allows selecting a range of characters in a line (or lines).
      • [CTRL+C] will copy characters if there is a selection.
      • [CTRL+V] will paste characters (replacing any selected characters).
      • [CTRL+ENTER] will exit editing mode and return to line selection mode.

Nano is a minimalistic but fantastic command-line interface text editor that can be found on most Linux distributions.

It uses a code library called ncurses, which I initially used to build the first versions of this project. Ncurses basically lets you define text and colors at positions on a grid, and it makes writing a text editor from (mostly scratch) a breeze.

Later I was curious about how Linux displays images, simple graphics, and command line terminals. So I began investigating the framebuffer.

In addition to using the framebuffer, I retrieved the hardware signals of my keyboard from Linux in the form of scancodes. From there I set up branches of code to account for how to react to different key presses and different combinations.

This experiment was incredibly fun, and was my first foray into interacting with hardware on a low level. Surprisingly, the biggest utility from this project was how it made me start thinking about hardware and software in a more comprehensive way. It really bridged a gap for me between my understandings of high level frameworks <-(&)-> the 1's and 0's that power, organize, and encode everything above. And seeing a bit beneath the hood closer to the machine processes has given me a lasting appreciation for the history of computing in general, and for all the sub-processes that have been working inside my computers all my life.

AutoHotkey
TreeView Tasklist

Written in AutoHotkey (AHK).
Built on top of my AHK GUI Abstraction Framework.

I use this for planning out my day,
keeping track of large projects & requirements,
and to minimize clutter while brainstorming.

AutoHotkey
Scrolling Notes with Nested Windows

Demo coming soon.

AutoHotkey
GUI Abstration Framework

Functional, but not tidy and not comprehensive in coverage, currently on the backburner.

This large framework powers the above two AutoHotkey projects.

This Website

A New Journey:

First time studying Web Design with HTML5 + CSS.

I'd never heard of the Jekyll static website builder nor the Sphinx documentation generator when I started, but I absolutely love both of these tools now.

Beginning with Jekyll and a Dream:

I had a basic idea in mind when I started, but I soon realized that if I wanted to make each page have a consistent <header> (the navigation bar and image at the very top), then I would be duplicating a lot of code. Coming from my background of using low-level languages, the notion of necessary code duplication was strange to me. But I was mainly worried about keeping it all in sync.

Imagine this scenario. Say I were to start with a homepage and a <header>. Then I make a second page. I copy the homepage's <header> code and paste it into the second page. Any time I create a new page, I need to make sure it contains this code (in order to have a consistent presentation as the user navigates between pages).

If later I were to change even one part of the <header>, then I would have to manually update the <header> code of each and every page to match that change.

This approach is not efficient and the product would not be easy to maintain.

So I searched the internet. I read that there were tools for building "static" websites, and I soon discovered the Jekyll. If you need to make a website, but you're not using a lot of JavaScript to dynamically insert content, I highly recommend Jekyll.

Briefly: Jekyll lets you define a basic layout, then you can wrap new pages inside the shared layout. You can define multiple layouts, and use variables to (a) determine what content to insert or (b) to generate content using your variables. It's awesome.

Code duplication exists once Jekyll bakes your website into an output directory. But in your source files, you just write code once (and tell Jekyll where it should be inserted). Instead of duplicating large sections, you just type {{ content }} or {% include some_file.html %} and you're done!

For an example, here is a link to this page's content file on GitHub: Apps.html. This is the file before Jekyll bakes it into the finished HTML page. You'll notice this file doesn't contain a <header> tag. When Jekyll bakes this Apps.html page, Jekyll takes the Layout_Default.html file and pastes the Apps.html file into the {{ content }} line inside Layout_Default.html.

Resulting in: the page you're looking at now!

If this is interesting to you, or if you'd like more details, you can read my initial impressions of Jekyll in [Blog Post #1].

Making Manuals:

Long story short, I love the websites for Godot's documentation and Blender's documentation. I knew I wanted to use the ReadTheDocs theme for Sphinx for my Documentation section from the beginning. It took a fair amount of time to learn Sphinx, Pygments, and reStructuredText, but it was absolutely worth it, and incredibly fun.

However I faced a few minor problems:

  1. I wanted to keep my <header> bar on my baked Documentation pages.
  2. I needed to find a way to take the HTML structure which Sphinx outputs -> and make it compatible with my Jekyll processing pipeline.

Automation:

After I discovered Sphinx, I had to be a little creative to make Sphinx's output files compatible with the needs of Jekyll's input files. I'm using Sphinx for the entire Documentation section, but I'm using Jekyll for all other pages.

To modify all my Sphinx-generated HTML files, I began automating a lot of manual tweaks and insertions by utilizing Regular Expressions for the first time. Then I reached a point where AutoHotkey was malfunctioning when performing Regular Expression replacements across huge files, so I began studying Python for the first time. I ported my AutoHotkey automation into .py scripts, and it's been amazing ever since. Now I can run these pre-processing and post-processing .py scripts from either Windows, or from my Debian WSL2 command line.

Creating Code-Block Folding:

Recently, I wanted to add code-block folding to my Documentation section. Sphinx was capable of fully highlighting my AutoHotkey code (after a few minor modifications), but had no support for folding regions of code. So I wrote a pre-processing script in Python to manually insert arbitrary tags near every open { curly brace and different arbitrary tags near every close } curly brace.

I wrote a post-processing Python script to take the HTML files which Sphinx generates, and replace my arbitrary tags with the HTML <details> element (which has built-in functionality for collapsing/folding blocks of text). It was really different for me, but pretty straightforward, and very fun.

You can try out my custom code-block folding here:

Short Writeup:

If you'd like to read more about the toolchains I use (and what my pre-processing and post-processing scripts do) I've written a short overview on this website's GitHub repository page.

In Summary:

I began this website with a portfolio concept, but I kept working on it because I really enjoyed it.




View changelog here.

Acronyms