Three Months of SwitchBoard
Written Wednesday, March 1, 2023
It's been about three months since I wrote Disability-Driven Development. I've written more about the future I think this could unlock, as well as the overall concept of what I'm doing with the SwitchBoard project. For this, I want to do something a little bit different - I want to tell a different angle of SwitchBoard's story. I want to share my commit messages.
One of the earliest things I learned about computer programs is that they are not actually for computers. Computer programs are for people. This is true of the software they represent - apps, web sites, presentation tools, and even printer drivers. But it's also true of the code that we use to "write" programs in the first place.
Far too many efforts in the digital technology space have forgotten that the point of all this technology is, after all, for people.
I've long held the opinion that "good code" is inseparable from code that people find pleasant to work with. But I think this extends far beyond the cryptic, squiggly-filled land of code itself. I extend this to the documentation I write for my software, to the essays I write about the theory behind it, and even to things like the notes I take while I do the actual programming itself.
It's common, on projects that involve more than one person, to use a particular technology called version control. When a team is involved, version control is a great way to collaborate, share work, and keep things from getting overly chaotic or stomping on each others' edits to the code.
At the moment, SwitchBoard is still a solo project - I'm the only one doing anything with it_, for now_. Some programmers would consider it overkill to use version control in this setting, but I like it.
Version control offers me two things that I prize, even when working by myself. One of those is basically a highly structured, organized "giant undo" button - if I get myself into a corner, don't like where something is heading, or otherwise just mess something up, I always have a complete history of what I've had before. Going back and "rewinding" is trivial, so it's much easier to feel brave about exploring stuff that might not work out. This experimental, exploratory style is central to how I work as a programmer.
The other thing that version control offers is record keeping. I don't have to make notes about how much I've done, or when I did a thing; the software tracks all of that for me, punctuated by moments I choose to cement in time - the moment of "commits."
Each commit can have a message attached to it. To me, commit messages are an invaluable source of context, and also just a plain fascinating historical archive of what's happened on a project over time. I use my commit history to remind myself I'm not actually dead in the water. Most of all, well written commit messages are pleasant to read.
I love microfiction, as a super-short story format. The challenge of expressing a complex, nuanced, often heavily-emotional idea in only a couple hundred words is fascinating to me - as someone who struggles with brevity in general. I love both reading and writing microfiction.
But my primary arena for telling dense, rich stories in a super compact format is commit messages.
What follows is the story - so far - of SwitchBoard, as told by commit messages. They're fairly heavy on the technical jargon, and told for a particular audience, but I hope they still retain a bit of the magic of a good narrative. I've even preserved all my original typos for authenticity, although sadly the process I used to get all the messages into this post seems to have obliterated most of my meticulous formatting.
Please, however, do feel free to just skim this - it is a lot; I promise I won't be offended if you don't want to read it all!
December 2022
Created the repository SpoonStack/SwitchBoard
First draft of recipe description markup, just to get some thoughts in a more concrete form (besides the literal paper on which most of this has existed up until now)
Initial commit of SwitchBoard UI experimentation, just getting some WPF stuff roughed in to play with.
Add initial .gitignore to keep VS cruft out of repo
Working on expressing recipe descriptions in C# based on the XML noodling that I've already done. Minor tweaks to make things make more sense.
Super rough beginning draft of some data-category specification markup.
Refining the drag and drop UI a bit more. Getting closer to a useful UX!
Incredibly hacky stub version of creating drag/droppable UI by reflecting over a Recipe object
Improving the drag/drop implementation a bit. Fixes a nasty re-entrancy bug that caused major performance hitches due to repeatedly moving UI elements around. Also adds a tiny bit of genericity to the drag helper code but I'm not clear how useful this is based on event-bubbling. Easy enough to remove later if needed.
Minor UI polish pass Removes the old test drag/drop rectangle. Changes dynamic UI population to occur after window loads. Centers dynamically created UI widget in the window, just for good looks. Slightly more useful types in WPF generator helper class.
Initial test hacks for consent model Recipes can be put into motion by "consenting" to the process they describe. This is handled (in implementation terms) via a Consent Facilitator, which effectively allows us to bridge the highly abstract notion of a Recipe with actual code runtime behavior. This change introduces a simple Reflection-powered Consent Faciliator (in a stub format) that lets us invoke a basic method on a target object, using the recipe description object model. The net effect is that we can call code on a .Net runtime object, given that the target object and method are described accurately by a recipe. This will of course need substantial amounts of work to turn into a truly general-purpose facility, this is just a starting proof of concept. This change also includes updates to the WPF UI generation code so that a recipe's UI widgets can be used to give consent to the recipe being put in motion. The final result is that the SwitchBoard demo can now describe a test object (the SwitchBoard window itself) using a Recipe object that names one of the window class's methods, and dynamically generate UI that allows that method to be invoked by clicking a button.
Initial serialization to Recipe Description XML format. Stubs mostly; just getting something in place so further refinement can proceed on top of an actual persistence model. Starting to merge the "noodling" XML docs with the C# object graph model, with the eventual intent of having the XML be a serialized/persisted form of the object graphs. Loading and saving these cookbooks will be extremely limited until we start expanding the range of the recipe system to include data category descriptions; but once that's merged in as well, there's a lot of cool things we can start doing with richer Reflection experiments. Incidental changes: Cookbook objects now have a name field. The Reflection consent facilitator now assigns its own name properly on construction. Tiny changes to the hacks in MainWindow to assist with serialization testing (actual file write is commented out in commit).
Clean up errant assembly reference from UIGenerator.
Refactor serialization approach. This migrates over to a Visitor-based model, which has a number of serious advantages over hard-coding the XML generation in the way I first hacked in. First, it means that XML creation logic doesn't need to get baked into every single concrete class in the object model. Instead, we can take a more generic stance in the object implementation (e.g. for things like recipe steps, ingredient types, etc.) and leave the serialization focused into one class. Secondly, it makes the process itself extensible to other kinds of cookbook traversal that are not just for writing data out; in other words, we now have a base process for anything that wants to go roam across the contents of a cookbook. This is especially interesting since it means we can, in principle, work towards creating a recipe visitor that itself invokes recipes, with each object in the cookbook's graph as ingredients - which would mean that the recipe model itself would become capable of deep and profoundly powerful metaprogramming.
Fix serialization bug that clobbered Ingredient counts.
Start UI generation refactor to use Visitor. As noted in a comment, this is still very work-in-progress; it's highlighted a weakness in the current visitor implementation that needs to be thought out further. However, it does demonstrate a pretty reasonable version of traversing a Cookbook object graph to produce UI that shows off the content of that cookbook, including the reflection-based Consent button. As an incidental test, adds some fake ingredients to the test recipe, just to stress things a tiny bit more. (This already revealed a serializer bug, fixed separately.)
Massively improve Visitor and UI generation. Main change is that Visitor now indicates when it is entering and exiting containers. In particular, this means that when an object in the Recipe/Cookbook graph has multiple container fields, the visitation implementation can tell when the process swaps between them. This has the effect of adding some additional tags to the XML format, which is fine; it costs a bit of extra space, but simplifies the schema and makes a lot of stuff easier. In the realm of UI generation, this makes rich UI layout based on object-graph topography much easier. The generated UI now looks more tree-like and legible, and is much more flexible than before. This change includes a few tweaks to Visitor to make it a bit less copy/paste heavy as well.
Added prototype version of user-input ingredients. This is a very rough first pass, but it demonstrates a few key properties of this system. First of all, by introducing a new Ingredient subclass, we can extend the way in which recipes obtain their inputs. Secondly, because of the generic Visitor system, this almost immediately gives us the ability to serialize the new ingredient class, as well as generate UI for it. In this case, we generate a simple text box that can be scraped prior to consenting to the recipe's process, as demonstrated by the plumbing in WPFGenerator. The net result is that our fake test recipe can now pipe input text from the automatically-generated UI into a function call which is invoked purely dynamically using Reflection. There's clearly a ton more to expand and refine here, but the mere fact that we can do so much nearly-automatically is already very powerful. Future interesting exercises might include smarter selection of the generated UI element by observing the Category and Constraint fields on the Ingredient itself; or building out recipes by using Reflection to inspect a class and dynamically generate matching UI for it.
Minor optimization to UI-ingredient plumbing. There's much more room for improvement here in general but this makes things a tiny bit less wasteful.
Start formalizing the consent mechanisms a bit more. Most notably, this explicitly begins to separate the idea of a "recipe" from the idea of "an invocation of the recipe." This gives us room to do things like have ingredients gathered up and then handed to a recipe's process as part of granting consent. This is an important split, analogous to distinguishing between a function's call site and its implementation, or between a class and objects of that class. We're still seeding the initial setup here so it will continue to evolve a bit, but the basics are in place. As of this change, the act of gathering ingredients from the (auto-generated) UI can be decoupled more nicely, and we're making progress towards totally representing the test recipe in the recipe model itself.
Simplify ingredient mapping a tiny bit. This folds the former "ConsentMapConstantIngredient" class into the ConsentMapFetchIngredient class, since the former can be expressed by using the latter with a lambda that just returns the constant value. The "Fetch" mapper is generally much more useful and likely to stick around; how we represent constants will probably evolve, so for now this simplicity is better.
Assorted minor cleanup pass. Mostly style fixes and minor adjustments to keep things nice and tidy. My knowledge of C# idioms is a version or three behind it seems :-)
Minor incidental cleanup to Visitor and UI generation.
First rough draft of generating recipes using Reflection. This is where things start getting really good! We can now pass a class (or object) to a reflection-powered system that traverses the class's methods and generates a cookbook full of recipes for those methods. These can then be invoked via reflection using the Consent facilitation model. Moreover, since these are Recipes in a Cookbook, we can also generate UI off of them, and serialize the descriptions. By itself this is a minor capability, but it paves the way to being able to seamlessly connect the runtime SwitchBoard UI with predefined C# classes (or anything in a .Net assembly, technically). Since Recipe traversal and manipulation are already implemented in C# objects, this is the first major step towards allowing SwitchBoard to edit its own code - effectively a form of self-hosting.
Stub in first version of SwitchBoard Console. This is just a trivial console app for now, but it will be a useful place to explore different modes of interacting with the overall Recipe system besides just GUIs. For now, it demonstrates a very crude parallel version of the reflection-powered demo (prompt for a message via appropriate UI affordance, then display the message). Instead of using WPF for this, though, it's all console I/O. Next step is to refine the abstraction a bit more.
Extract Reflection test object into its own library. This centralizes the code used for the Reflection tests into a shared location and makes it slightly more abstract, to remove the tight coupling to specific UI approaches. The shared reflection test object retains the attributes that allow the Recipe generator to plumb in parameters/etc. via UI, but now we go a step further; the output is handed to a basic Action delegate as well. This delegation fully separates the ReflectionTestClass from any specific UI implementation details, allowing the same core class to be used by both the WPF demo and the Console demo. The net result is that the same recipe, generated from the same shared library, can be connected to two very different UI environments trivially. By itself this isn't much more than just good logic/presentation separation - i.e. basic good software architecture. The real magic will start when we can do things like plaster UIs on top of arbitrary classes without needing invasive markup or explicit delegation.
Refactor implicit-process setup hack into shared code. Right now, in order to seed the Recipe interaction model, each of the test harness UIs (WPF and console) creates an "implicit process" without an associated recipe. This process is populated with step(s) used to consent to the auto-generated recipes we're testing on. Moving this code to a shared location eliminates some redundancy and also gives us a solid option for refining how that consent process is set up in a slightly more generic way.
Assorted minor cleanup. Restructure folders a tiny bit to make the layout a bit more sensible. Also cleans up a few minor code tidiness issues (which mostly come from my C# muscle-memory being a bit out of date still).
Incremental refinements to Reflection recipe generator. This change adds some new recipe classes as well as C# attribute markup for UI affordances, both as recipe ingredients and products. It also adds some support for mapping C# type information in method parameters/return values into recipe ingredients and products, respectively. As the ability to work with type metadata is added to the overall recipe infrastructure, this will enable better safety checks and richer input/output mapping when consenting to processes.
Trivial - cleanup unused function parameters
Add "Catalog" class for caching common stuff. For now this is mainly used by the Reflection-based recipe generator to reuse objects for things like C# types expressed as Data Categories. Long term this will be important for both efficiency and correctness (i.e. making sure we don't have multiple representations of the same underlying construct in the object graph at runtime). This change introduces a Utilities project for shared, general-purpose code that is best not tied to a particular project. It'll probably accumulate a lot of other, similar classes over time.
January 2023
Introduce the Dispatcher library. The Dispatch Core is meant to be effectively a scheduling/coordination kernel that primarily exists to facilitate consent exchanges between Recipes. This of course means it may hand off to other consent facilitators freely (e.g. invoking code via Reflection) as well as handling some degree of control flow and logic in the Recipe layer. An easy way to think of the Dispatcher is as the pump that keeps things moving forward; but the idea is that, over time, it can become much more sophisticated, and also handle things like coordinating parallized workloads (even across asymmetric processing units or distributed nodes) as well as scheduling resource usage to satisfy resource usage concerns. Obviously this beginning stub is a long way from that though :-)
Distinguish between Canonical and Display names. Mostly affects Data Categories at this point; also includes a few minor additions to the Visitor system to enable this to work cleanly. Net result is that the UI presentation of a Data Category obtained via C# Reflection can now look cleaner, i.e. using the type name "String" instead of the fully-qualified, assembly-tagged type signature.
First pass of proper ingredient mapping. When we consent to a recipe's process, we can provide any subset of the expected ingredients at the same time. This general-purpose approach means we need a way to route the provided ingredients to the appropriate "logic" or "code" eventually; this responsibility is specifically placed on the Consent Facilitator, since different facilitation models may require different handling of the mapping. For example, the Reflection facilitator uses the ingredient order as provided in the target recipe to map incoming ingredients onto function parameter values. Eventually, we can use this kind of rich mapping support to do things like partial function application and lazy evaluation; this enables us to consent to logic implemented in, say, strict functional languages like Haskell, or to do cool things like hold function invocation until all responses return from a distributed process. The Dispatcher model will be a central home for experimenting with the power of this strategy going forward.
Minor tweaks to Reflection consent facilitator. Clean up a possible-null-deref warning and add a comment for future work.
Improve Reflection consent facilitator a bit. This includes some better failure cases (throwing exceptions instead of silent no-op) and shores up the method-matching a bit more so that it can handle things like overloads with similar parameter counts but different type signatures. As a knock-on effect, this also changes the test case a tiny bit to have an overloaded method so we can verify the functionality works. This has revealed plenty of further room for improvement across the system, but this is a good place to commit for now!
Trivial null safety check.
Slight improvement to behavior when calling overloaded C# methods. This is still very hacky, but it prevents an omitted (null) ingredient from being passed as a parameter to an overloaded method that has both value and reference possibilties.
Trivial cleanup to Visitor.
Fix some assorted quirks and bugs in WPF UI generation.
Add static function to Reflection Test Class. This helps stress some recent UI generation changes.
Add Visitor support to Data Products. In practical terms, this trivial change enables both UI generation and serialization on recipe products that are data.
Tweak recipe gen to ignore built-in C# object methods. This cleans up the UI and serialization somewhat, but is not a long-term solution, since clearly there will be use cases where those methods should be included.
Tweak default UI size to be a tiny bit more convenient.
Playing around with collapsible UI.
Fix ingredient fetching from generated UI.
Initial stub work for handling recipe products. This is very rough and there is a long way to go before this is generally useful, but it's a start! We can now return values from C# functions (when using the Reflection consent facilitator) and treat them as Products in a recipe chain. Next step will be to actually plumb those outputs into ingredients in successive recipe consent steps, which will allow us to do basic functional composition.
First sloppy version of functional composition. This change modifies the tracking of Products during Recipe dispatch to use a Dictionary instead of a List, so that remapping Products to Ingredients in other functions is much easier. This also adds some Consent Step mapping functionality to allow function composition, i.e. forwarding along a product of one recipe as an ingredient to another. This is super hacky and limited at the moment, but with a small bit of refinement, will open up the doors for implementing actual interesting logic in recipes - especially once we add higher-order function equivalencies in the recipe system!
Minor refactor - remove extraneous member variable
Trivial cleanup (simpler C# syntax)
Improvements to Consent mapping setup. Introduces a Builder-pattern helper class that can be used to assemble consent chains and ingredient mappings to facilitate basic functional composition.
Improve handling of static functions. Cleans up some hacks and splits Recipe names into Display and Canonical variants; Canonical names are used to encode type signature data so we can do things like find the required class when invoking statics. This is still a little hacky, but it's a bit better than things were.
Expanding functional composition experiments. This refines a few minor aspects of the pipeline as well as reattaching the basic UI input mechanisms for both the WPF and console test beds.
Minor convenience boost for function composition.
Assorted code cleanup. Centralizes a bunch of logic related to C#/.Net Reflection into a single library, ReflectionRecipeSystem, instead of leaving reflection-related logic scattered in various places like the Recipe Description Model base library. This will make it easier to manage dependencies in the long run, as well as shoring up the potential for decoupling the ecosystem from C# eventually. Various trivial formatting tweaks including whitespace, removing dead comments, and cleaning up Using statements.
Refactor/clean up the way C# types are wired to ingredients.
Serialization for Recipe Processes. This has been tested with the hack-generated implicit process stuff, and verified to at least get to disk properly. This paves the way for deserialization, so we can store these compositions on disk and not regenerate them in hard-coded logic every time we start up. Once we can start getting UI wired in to play with composition at runtime, the SwitchBoard interface will then be able to "write" and save programs in Recipe form, in addition to just running them!
First pass at generating UI for Recipe Processes. This is extremely crude but a decent starting point for visualizing recipe process steps. It's already informed a handful of possible improvements to the WPF generation scheme which is great!
Massive improvements to UI generation. The WPF UI generator now implements a more context-sensitive approach to understanding the things thrown at it by the Visitor object as it traverses a Recipe object graph. The net result is that we can do much more specific UI layout based on what we're actually generating widgets for. In particular, this leaves the standard "recipe description" UI as it was, while changing the Recipe Process generated controls substantially. The net result looks a lot better and has a bunch of custom wording and such to make it really flow nicely.
Show more recipe stuff in the main UI. The implicit recipe and the cookbook (generated by Reflection) are both now displayed by default when starting the WPF UI version of SwitchBoard.
Put a border on recipe UI for better visual clarity.
Cosmetic refinements and polish to UI generation.
Tiny bit more cosmetic polish to UI generation.
Fix indentation bug in UI generator.
Assorted UI refinements. The main adjustment is to the WPF UI Generator; this change adds a way for callers to "decorate" generated UI elements. This allows the main window UI to inject a "give consent" button onto any recipe that has a process that can be consented to. At the moment, that just means ones built by the composition test code in ReflectionRecipeUtilities. There's also a lot of small bug fixes and code cleanup to the UI generator, and some minor visual polish. This change also incidentally adds a new recipe composition test case (which just directly routes the static test function's return value into the output function) to help stress test the UI. Minor XAML cleanup as well.
Trivial code cleanup.
Interim commit - adding editor UI. This forks the UI generation system into a "viewer" mode and an "editor" mode, which handle the recipe object graph in slightly different ways. The editor UI will eventually become a general-purpose recipe editing mechanism, in contrast to the read-only viewer UI. Includes minor refactors to facilitate all of this, and some incidental fixups to the drag/drop hackery going on in the main SwitchBoard WPF window.
Minor editor UI iteration. Adds a dropdown selection list of known recipe names in place of a freeform text box when creating UI for a "give consent" recipe process step. This is a minor but interesting step towards having the underlying recipe be mutable via UI interaction. It is very unlikely that the dropdown list is a usable long-term option here, but it's a helpful starting place, and we can always refine as we go.
Dispatcher - forward recipe consent facilitation when appropriate.
Add "give consent" UI back in. This is now split across two modes: the basic "Give Consent" button that is used by the Viewer UI (works as before), and a series of "Test" buttons that appear on the Editor UI - one for each recipe, as well as select individual Process Steps. As usual, this is a very rough starting implementation, but it demonstrates the potential for finer-grained interaction with recipes and fleshes out the UI generation paradigm a tiny bit more. Longer term, this extremely crude system could be turned into something of a live-editing/REPL style UI without much fuss.
Start roughing in support for local variables in Recipes. This uses a concept called a "scratch space." The notion of a scratch space is deliberately abstract. In some languages, this might be comparable to a local stack of variables. In others, it will be more like a free-store or heap store. In yet other contexts (e.g. pure functional models), scratch space is just a way to account for temporary data but is not meant to provide mutable storage, and so on. By abstracting over the concept of scratch spaces, we can provide flexiblity over how ingredients migrate around a running program, without losing any transparency about the storage they consume or their lifetimes.
Working on editor UI for scratch spaces. Basic variables in a scratch space can now be listed in the editor UI. The widgets don't do anything yet, but they at least demonstrate the ability to see scratch space ingredients in a recipe, as well as the ability to create named and typed variables inside those spaces. Until there's a way to map these variables as ingredients when giving consent to recipes, this is about as far as the UI can usefully go, so that's probably up next!
Initial variable support. Scratch spaces can now be used to store and retrieve ingredients (as long as they're data). This change includes basic Viewer UI but no corresponding editor UI yet. Changes the recipe generation hacks a bit to stress test this use case.
Minor safety fix to UI generator. Ensure that shared column-size tag names are filtered to fit the (rather inexplicably strict) criteria that WPF wants for them.
Add placeholder Editor UI for "store variable value" recipe steps.
Trivial cosmetic polish.
Add File menu and Save/Exit functionality to WPF UI.
Serialization refinements. This includes a fair number of small changes, including some consistency fixes to the way Scratch Spaces interact with the Visitor, a new "Library" which can contain both loose Recipes and entire Cookbooks (along with Visitor and Serialization support), and minor supporting UI tweaks. This also sneaks in a change to the way we encode canonical recipe names for things we obtain via Reflection; the name structure is now "FunctionName@ClassName, AssemblyInfo" rather than tacking the Function name on the end of the string, to make serialized names a lot more readable.
Start stubbing in deserialization support. Recipe XML files can now be loaded using the new File/Open menu command. They are expected to match the saved format from the serialization system (available at the moment via "Save As"). This implementation is very preliminary and likely to evolve quite a bit over time, but it at least gets the basics of loading recipes going. Most of the actual interesting loading work is still missing and will follow in subsequent commits. The final goal is to be able to round-trip a full Library full of recipes and cookbooks - that is, to take a known-good object graph (for example, as fabricated by the test code), save it to XML, load that XML back in to objects in a fresh process, and save the new object graph to XML. If the serialization process is fully working, the output of the second save will be bit-for-bit identical to the original.
Trivial cleanup - remove unused Using declaration.
Massive work on deserialization. The main accomplishment of this change is that we can now generate recipes, save them to disk, load them up, and save them again without any loss of data - i.e. we can successfully "round trip" our entire test cookbook! This is a major landmark in the process, since it means we can start persisting data outside of the test environment, and slowly begin removing the seeding hacks that were used to get things up and running. Of course, automated tests to verify this functionality will be instrumental to avoiding regressions and bugs moving forward, but that can come next. To facilitate all of this, deserialization logic has been moved into a full class, to allow for stateful tracking of the load process. Quite a bit of code has been adjusted accordingly, and the remaining functionality for loading recipe object graphs from stored XML has been written. There is a ton of potential for refactoring and cleanup and improvement in the entire serialization/deserialization system at this point - but that can wait until after we have regression-proofing in the form of a test suite. Also notably, deserialization at this point is extremely fragile in the face of malformed input. This deserves a lot more care and attention going forward, but we have to start someplace! The Reflection/Recipe bridge code has been augmented in a few places as well, to make deserialization easier and to correctly resolve ambiguities around things like overloaded method names (which need unique canonical recipe names). On the UI front, automatic recipe generation has been attached to a dedicated menu item (so it can be done on-demand instead of on SwitchBoard startup). Moreover, the File/Open menu command now generates UI based on the contents of whatever is loaded from disk. Incidental changes:
WPFGenerator now supports entire Library objects (via CreateAllUI) as well as custom post-process delegates, which MainWindow uses for things like centering UI elements and attaching the drag/drop hack test logic.
Recipe product names are now serialized.
Fix some consistency bugs in serialization of data category references.
Minor cleanup and tweaks including null-dereference protection to keep the tools happy.
Minor editor UI updates. This replaces some placeholder text box widgets with actual drop-down selectors for the editor UI for "store variable" recipe process steps. None of the editor UI does anything, yet, but we're getting closer!
Add "delete" button to Scratch Space variable list. This is a very early start to having the Editor UI actually do interesting things. The Delete button basically allows editing of an active recipe in a very specific way (i.e. removing pre-existing Variables from a Scratch Space ingredient) but helps to illuminate a few significant changes that need to be made to the Editor UI implementation overall to make it really usable for editing in general. This change is the first step along that path. Plenty more iteration and refactoring can be expected as we go along. The important thing is that we can now selectively poke Recipe object graph contents and live-update the UI to remain correct after such changes are performed. For the immediate future, turther Editor UI work is likely to largely consist of making this functionality more general-purpose and more robust, in small bits and pieces at a time.
Split automatic test generation stuff into three parts. This moves the previously monolithic "generate test project" functionality into menu options for invoking the reflection-based recipe generator, creating the recipe composition test case, and creating the editor UI test case. Testing stuff is now smoother and simpler as well as easier to focus where desired, especially since serialization can be used as a backup for "good starting point" generation too.
Trivial simplification.
Add "File / New" menu option. As can be expected, this basically just blanks out the current project state and starts fresh. As can also be expected, an "unsaved changes" warning will be coming soon... once I lose enough work to not having one.
Minor progress on editor UI. This change tweaks the Visitor to optionally allow visitation of empty containers; the Editor UI generator uses this to create stub sections that will in turn allow creation of new entries in formerly-empty containers. Also adds a "Create" menu with an item for making a new, empty recipe. Editing these recipes is still very limited, but as a step forward, a Process (set of steps) can be assigned to a recipe that does not already have one.
Editor UI - implement new "add" buttons. The Recipe Editor UI now has buttons for adding Ingredients and Products to a recipe. These will actually mutate the underlying recipe, although the functionality is at present still very limited. Adding a new Ingredient will actually add a "proto" ingredient, which is a special Editor placeholder that is used to prompt for an Ingredient type selection. Once this selection is made, the proto ingredient is replaced with a proper ingredient of the selected type. This is just experimening with some UI interaction flow to see how it feels.
Trivial null-safety improvement.
Improve Visitor logic for general containers. Whenever we start visiting a container, relay the container object itself (via HandleContent with a null ContentName). At the moment, this has no visible consequences. However, it will enable a series of refactors to make the Editor UI generation much cleaner.
Fix redundant ScratchVariableData traversal in Visitor logic.
Editor UI - factor Variable handling into a user control. As part of the ongoing Editor UI work, this splits Variables in a Scratch Space into their own dedicated UI element. This reduces the amount of ad-hoc code used to generate widgets, as well as dramatically simplifying the code needed to live-update the UI as edits are made.
Minor editor UI polish (but hacky). These hacks prevent the drag/drop test logic from flaking out on the Variable editor widgets, and add a "Select Data Category" hint to the category dropdown. They're not good implementations so they'll get replaced eventually, but they make the UI a bit nicer to use.
One more minor UI fixup hack.
Add "renamer" control and use it for Variable editor UI.
Numerous UI refinements. Editor UI for Scratch Space ingredients now uses the Renamer control to allow easy renames of those ingredients. Recipe names are also now attached to Renamer widgets. The Renamer has been given the ability to apply text styles as used by the WPF UI generator, and these are set up to match the visual styling old Labels generated in the editor UI before this change. Also included:
Renamer control now supports Enter/Escape for Apply/Cancel actions, respectively.
Renamer focuses the text edit box and highlights its contents on beginning a Rename operation.
Visual polish on the Renamer control, including better automatic sizing.
Fix up the drag/drop hack a tiny bit more to avoid unwanted dragging when interacting with controls.
Assorted editor UI refinements. This change splits the "viewer" UI for Ingredients and Products into internal details of the Viewer Generator class, and creates new implementations for the Editor generator. At the moment, they behave the same as before, but this is a good stepping-stone to replacing them with full user controls. This also cleans up some of the logic around ingredient and product UI generation in general for consistency and simplicity.
Extract data category selection into its own user control. Incidentally starts relocating "known object tracking" into a more central/clean setup, but that is only partially finished. Also includes some related touchups to DataCategory to make it easier to display directly in WPF controls, and minor cleanup. This is part of the ongoing work to extract recipe editor UI into more maintainable and well-organized pieces.
Fix editor UI bug with disappearing "add ingredient" button.
Fix recipe pickers in editor UI.
Minor fixes for known object tracking.
Editor UI - user control for Data ingredients/products. At least for now, the basic role of Data Ingredients (as well as Data Products) is similar enough that both can be edited using a single UI element, which has been factored into a user control for easy reuse. As a bonus, since this new user control basically just wires up two existing controls (renamer and data category picker), this change basically makes both Data ingredients and Data products fully UI-editable with very little work.
Editor UI additions and consolidation. This change adds "delete" buttons for Ingredients and Products, building on the existing pattern used for Variables. This also removes the redundant "Edit Variable Data" user control since that functionality is now folded into the more flexible Editor General Data control. By centralizing the logic, we increase overall reuse and simplicity, and ensure consistent behavior across all elements of the Recipe editing UI that involve "things with Data Categories."
Initial stub support for adding process steps to a recipe. This adds a few bits to the editor UI for allowing process steps to be added to an existing recipe. The generated widgets don't do anything yet; they'll be getting replaced with proper editor user-controls soon anyways. Next step is likely deletion of steps.
Add deletion buttons for recipe process steps. This is just a temporary hack; it's much more sensible to just extract the entire editor UI for steps into a series of user controls instead, following other patterns used recently by the editor UI. I'll be doing that next. In the interim, I'll be taking this as a sign that I need to stop and eat something, because my decisions are getting dubious.
Minor dead code removal. Eliminating some unused things to make it easier to focus on what's actually needed for the moment. They're easy enough to add back in if that ever becomes a useful thing to do.
Interim work - add user controls for consent mappings. When we give consent a recipe to perform its process, we need to pass along the relevant ingredients; this is done via consent mappings. This change begins extracting some of the UI widgets for editing those mappings into user controls. However, there are several notable limitations to this change - it is not complete:
The mappings cannot actually be edited using the widgets yet.
Some amount of the UI for "store variable" steps is slightly disrupted by this change; this will get fixed when a user control for the actual process step is implemented.
The logic around "give consent" recipe process step UI generation (in the Editor) is messy and bad right now. This will also get cleaner with the introduction of a relevant user control.
Interim commit - add process step user controls. This adds some basic stubs for the Give Consent and Store Variable process steps in the Editor UI. These still don't actually do anything and there is a lot of cleanup left to be done, but it's moving in the right direction!
Interim commit - working on Editor UI. Lots of changes here. Adding a bunch of user controls for various bits of editor UI, which can be bound to underlying recipe object graphs to actually change their contents. Some existing composite widgets have been updated to use these selectors. Also includes a few supporting tweaks to the recipe graph (giving things ToString() functions as appropriate).
Use Recipe Picker user control in Editor UI generator.
Fix variable picker to update variable names when a scratch space is selected.
Add ability to create new consent mappings in editor UI. This is the last major remaining piece for being able to fully edit a recipe object graph (as it stands today at least) in the editor UI. There are probably bugs and other things to touch up, but this gets us to the point where we can stitch together an entire recipe, process and all, using the UI.
Trivial visual polish.
Editor UI restructuring. A lot of things in the recipe object graph are of the form "list of stuff that can be added to, removed from, and potentially rearranged." This change is the start of a restructure that pulls this "general editable list" concept into a generic user control. For the first pass, the "editable list" only supports item deletion. More general-purpose addition and rearrangement functionality will come later. At the moment, the "Give Consent" recipe process step editor UI is a bit of an outlier and badly in need of a rewrite to also be handled by a user control, but the basic prototype works. Some key refactors are coming that will simplify this quite a bit more.
Minor editor UI fix. I really need to rewrite the awful UI regeneration hack I'm using, it's becoming a severe liability.
Reflection Test Class - add some stub functions. This is the very first seed for a basic concept I have for a demo, which amounts to "average a list of numbers." The idea is to slowly expaand this idea into more and more sophisticated implementations, until reaching a point where it is a full (but tiny) GUI application. This just gives us two basic pieces: a way to get a list of numbers, and a way to compute their average. Both are implemented as functions on the Reflection Test Class for now, and we'll explore ways to flesh this out over time.
Assorted minor code cleanup.
Add Edit/Commit buttons to recipe Viewer and Editor UIs. This makes it possible to take a recipe that is shown by the Viewer and begin editing it, and also "lock in" edits to a recipe by converting its UI to a simple viewer.
Introduce "Undo" button to Editor UI. This rolls back any changes made by the editor to the state of the recipe when the UI was built up, and reverts the UI to a Viewer. Includes a handful of supporting tweaks to Serialization and the Main Window logic to make this work properly.
Initial stub of "Recipe Tester" UI. This pulls some hacky stuff from the existing UI into a dedicated "Tester UI" with its own WPF generator and everything. The general idea is that the Tester UI will provide ways to hook up ingredient values and stash products, creating a sort of REPL-style environment (along with some other things that will need to get added as we go). This doesn't do much of anything yet, but it does demonstrate that things can work with recipes that have no ingredients. It sets us up nicely for more refinement soon though. It also paves the way for removing some of the more smelly hacks around piping UI elements into recipe ingredients.
Major work on Tester UI. There's a lot going on here! The most important thing is that the Tester UI can now be used to pass along basic ingredient content for Data ingredients. It's limited to pulling strings out of a text box for the moment, but this gives us a really good starting place. Another major addition is the ability to relay ingredients from a recipe to secondary recipes when doing a process. This allows for a form of what is essentially partial function application, among other useful shortcuts for piping ingredients around. This also means we can remove the old hacky UI attributes and other stuff that was gluing together the WPF demo before. The result is a much cleaner bit of code and a better experience overall from the UI side. Also included in this commit are tons of little fixes and tweaks to eliminate quirks and bugs from the UI in general. There's too much to list and we're not being super-formal about tracking this project so suffice it to say the UI is way less wonky now! There is still a long way to go in making the UI implementation less egregiously hacky, but this is, as usual, good progress.
Big chunk of UI generation refactoring and cleanup.
More minor UI generator cleanup.
Fix Editor UI Undo button. Previously, the Undo button would lose track of the "pristine" state of the recipe after every change, allowing only very limited rollbacks, and losing changes if it was used after multiple successive changes. This alters the behavior to only take the "pristine" state snapshot once, and then carry that forward until a Commit.
More UI code simplification.
Flag future work item.
UI code cleanup - less tight coupling to specific widget classes.
More cleanup work. Removes a bunch of code and files that were stubbed in early but are not currently useful. This helps declutter things. Plus, a lot of that code isn't really keeping pace with the way other stuff has been evolving, so it's just debt at this point.
Deserialization will now at least throw exceptions instead of failing silently. Better handling still needs to happen someday.
Some automatic generation stuff has been removed, to encourage me to use the create/save/load flow more heavily and keep working out the nuisances and glitches and bugs there.
The SBConsole app is currently a bit gutted at the moment, pending some rethinking of what I want it to be all about.
Attempting to simplify the way the drag/drop code detects what to treat as "draggable" versus what shouldn't be. This may need more refinement in general but it seems OK enough for now.
The drag/drop "preview" shape is now sorted to the top of the Z order correctly, so that it shows up over all other objects while dragging recipe cards around, as intended.
Fixed a bug where the Forward Product UI wasn't fully attached to the correct recipe object graph nodes.
Marked some additional items for followup later.
Map products correctly when giving consent to other recipes.
Trivial refactor to the code that clears out the UI.
Trivial cleanup.
Trivial cleanup.
And some more trivial cleanup!
Improve the handling of editable recipes in the UI. Not all recipes can be edited. For example, anything imported, or generated from a Reflection generator, should generally be considered "fixed." The main window UI now has an allow-list of editable recipes. Any recipe created by the UI is automatically editable. Moreover, this characteristic is saved into the Recipe XML files upon serialization, allowing persistence of this property. The "editable" status of a recipe is currently persisted to the XML using a new mixed-in node called "FlavorData." The general point of flavor data is that anything can annotate an existing recipe object graph freely, and transmit those annotations as it sees fit. This is meant to be thought of as a facility baked into the recipe model itself, albeit deliberately not explicitly represented (so it can remain flexible). Another useful future possibility for the Flavor Data is to store the locations of Recipe card UI objects if they've been dragged around to new locations, and so on. This change includes a few additions to help make this work, including a "canonical name" fixup utility pass which can be invoked to ensure that all recipes have valid canonical names before serializing them. This is especially helpful for the editor "flavor data" since recipes are referred to by canonical name and not display name.
Fix bug with drag preview selection getting blown away by UI clear operations.
Clean up Editor UI generation for Give Consent recipe process steps.
Initial stubs for the "Stash" system. The Stash is a sort of holding tank for assorted ingredients and products that can outlive a specific chain of Recipe processes. It has a viewing UI on the main window that can be used to see what's in it; eventually this will allow richer interactions as well. When a recipe process is consented to via the Tester UI, any products are added to the Stash as a temporary demo. Going forward, this will give us an ability to play with recipes on the fly. The Tester UI will be updated to be able to optionally shunt products into the Stash, and to draw Ingredients from the Stash as well. In effect, the Stash will offer a kind of "working memory" that has the lifetime of the SwitchBoard session, thereby making it the loose equivalent of a Read/Eval/Print Loop facilitator for the Recipe metaprogramming model.
Recipe Tester UI: add ability to control what happens to Products. At this point, there are three choices for what to do with each product of a recipe that's being used in the SwitchBoard UI. It can be summarized in the "completion report" message, placed into the Stash, or discarded entirely. This sets up the possibility of messing with a recipe until satisfied with its results, then saving them off to the Stash to continue working with separately.
Add stub for Ingredient sourcing to Tester UI. This replaces the old horrific UI plumbing hack with a proper mechanism that can route arbitrary user input to a recipe product. For the moment it only works on strings, but adding conversion layers (and specialized UI etc.) should be straightforward enough over time. This also stubs in UI for a "get ingredient from the Stash" functionality, but that's currently not hooked up yet.
Initial support for sourcing Ingredients from the Stash. This change adds a cleaner, more permanent representation of the Stash concept, and tidies up some of the existing UI and logic around it. It also wires up the Recipe Tester UI to be able to actually grab stuff back out of the Stash for passing along to recipes. There are some hiccups and rough edges in this implementation, but it mostly works, and is a great start for further refinement over time.
Fix Stash storage class to automatically live-update the UI.
Add ability to name Products that are getting put into the Stash.
Messing around with adding an "Import Assembly" feature. The basic idea is that we want to be able to use Reflection to load up existing .Net assemblies and use them as recipes. This is a very rough start to that goal; it will mainly be useful in terms of helping pinpoint places where the existing UI needs more work, as well as the underlying infrastructure. Two key realizations have already come from this: the way cookbooks are handled in the UI really needs to change soon; and the way we reference immutable recipes (like Reflected assemblies) should really be built around something more indirect instead of importing all the recipe stuff direct into a Library - i.e. an actual "external reference" mechanism. Expect those things to evolve in the near future. Also includes some incidental cleanup to the Stash system plumbing.
Ensure that imported .Net assembly recipes are tracked properly.
Start refactoring UI creation into the UI Seeds module. This extracts the actual creation of widgets into a shared library called UI Seeds. A minor benefit of this change is that the WPFGenerator class hierarchy is no longer needed in order to do runtime widget generation, although this is admittedly not important by itself. It's mostly convenience and introduces coupling so that building SwitchBoard will also keep the UISeeds binaries up to date. What really makes this a powerful change is that the UI Seeds assembly can be directly imported into SwitchBoard. In other words: recipes can now be used to generate basic WPF UI! As an incidental change, this also moves us away from using Label controls for everything, and using TextBlock more directly, which is just cleaner and simpler since we're not using the more complex functionality of Labels at this point.
Tester UI - don't offer ingredient pickers for non-Data ingredients. This fixes a rather weird situation where the UI prompts the user to input a value for Scratch Space ingredients, which just plain makes no sense.
Add "load variable into product" Recipe Process step. This includes serialization/deserialization as well as editor and viewer UI generation. The step itself basically just pulls a variable out of a scratch space slot and yields it as a product from the active recipe process. It's a sparkly, fancy "return" statement.
Minor improvement to suggested Stash names for products.
Add a recipe process step for yielding a product from another recipe. Includes UI and serialization. This step allows basic relaying of recipe products up the chain of consent. It's less hefty than needing a full-blown local variable on a scratch space and fits more neatly into functional-style recipe compositions.
Refactor some Editor UI generator code. This coalesces the shared logic of several different Recipe Process Step generator contexts into a shared base class. This change also incidentally fixes a previously undiscovered bug where process steps that have an nested/inner Give Consent Step would confuse the UI generator and create duplicate regenerated Editor UI widgets when the outer step got deleted. Yeah this code is all a giant hack. Hence all the refactoring :-P
Clean up a minor hack in invoking code via Reflection.
Extract notion of a "project" into its own class. This pulls a bunch of assorted stuff out of the Main Window UI implementation and makes it a bit cleaner. It also gives us a centralized place to eventually expand on the notion of things like collecting recipes imported from external locations - such as .Net assemblies scraped with the Reflection Recipe Generator.
Store imported .Net assemblies as part of project data. This allows the use of Reflection to import recipes and cookbooks from an existing DLL of .Net code on disk, and then relying on those recipes to build purely SwitchBoard recipes. This has been verified with a round-trip serialization test as well as experimentation. Also includes an incidental improvement to the Reflection-based assembly importer so that hidden classes are not imported as cookbooks. This cleans up the import experience substantially.
February 2023
Omit non-Data ingredients in cases where we need Data ingredients. This improves the UI for ingredient pickers as well as avoiding some wasted work in the Tester setup.
UI work - implement consent mappings for implicit Consent steps. Certain recipe process steps are currently implemented by wrapping an implicit "Give Consent" step. This change adds the ability to put ingredients mappings on those hidden steps, basically allowing parameter passing etc. The entire "wrapper step" construct is likely to go away once actual product mappings are in place, but for now it works decently enough, and this change helps make the code a bit more general regardless.
Hacky experiment: wire up ability to have generated WPF UI invoke a recipe This enables a (very shaky) demo where a SwitchBoard recipe can create an entire window, including a button, and have that button invoke another recipe when it is clicked. It's a very rough first start. I hope I've emphasized that sufficiently. But it demonstrates that we can have logic implemented as recipes that interacts with UI generated by other recipes - a very potent start to making full UI-driven programs in SwitchBoard!
Interim commit - map products after giving recipes consent. This is a fairly significant shift away from using various forms of Recipe Process Steps to handle Products, and towards using Consent Mappings instead. In particular, this removes a couple of steps and replaces them with equivalent mappings. There's a ton of associated UI cleanup and adjustment as well as some tweaks to make serialization a bit cleaner and more consistent. It (kinda) works as is but there will be plenty of things to tweak and fix up in the aftermath. Just wanted to get this committed to move things forwards.
Fix for handling unmapped products reasonably.
Add events and event broadcasting. Events are specific, transient things that occur. They are described by EventTags. An EventTag can be used to set up a Responder, which automatically gives consent to a recipe when an event matching a particular tag occurs. This gives us the basics for an asynchronous dispatching mechanism to go along with the more linear, synchronous stuff that already exists in DispatchCore.
Flag a few future work items
Interim commit - begin UI and serialization support for Event Responders. There's a lot more that needs to be done before this does much of anything, but it's all the progress I can manage for the moment.
More work on event responders. This fixes up some serialization/deserialization bugs and gets the editor UI to the point where it at least mostly works. Viewer UI is omitted for now, favoring just generating an editor instead; this is a hack for the moment because I'm conserving energy and don't feel like putting in a Viewer just yet. I'll probably clean it up later. The biggest missing piece right now is that new event tags can't get created in the UI at all. I need to think carefully about how to make that work.
Major UI refinements! SwitchBoard is starting to look like a real thing! This change adds a Project tree-view to the left side of the main window, moves the Stash UI to the bottom of the window, cleans up some visual styling, and replaces some hacky menu items with something a little more permanent-looking. There's still a lot of polish to be done here but this is a very pretty step in the right direction.
Add an empty RecipeProcess to manually created Recipes. This is a minor quality-of-life change. Since it is not really meaningful to manually create a recipe that doesn't do anything (at least, not right now), it makes more sense to just go ahead and seed new recipes with a Process. This eliminates a button click and makes the flow a little smoother and more intuitive.
Introduce "draggable panel" UI element. This is a sort of container-control that sits on the main SwitchBoard editor workspace. Instead of each recipe, responder, tester, etc. just being a colored card that can be dragged around, this control provides a consistent visual language for the overall notion of "thing in a workspace that can be moved." There'll be more work later to refactor and clean this up, as well as adding various affordances to the UI to make working with these draggable cards easier. For now, this is a nice step towards a more robust UI paradigm for SwitchBoard, and also paves the way for even more sophisticated UI setups in the future, including things like tabbed document editing.
Minor cleanup, flagging some future work items.
UI refinements for Project Tree. This change coalesces all of the "project tree" logic into a self-contained user control. Additionally, this adds "double click" handlers on certain tree nodes, which pop new viewer UI instances into the active workspace canvas. This allows the cards for recipes, cookbooks, and so on to be "closed" and reopened at will. At present there is no exclusion on having multiple copies of a card open; I may revisit this someday, but for now it feels kind of usefully freeing.
Stash recipe products by default. This tends to be what I want 99% of the time so I'm just saving myself some extra clicks for now. Will likely evolve this more someday.
Fix bug in deserialization of Load Variable consent mappings.
Fix known-object tracking for Responder objects.
Fix event tag picker control.
Improve manual ingredient handling in Tester UI. Instead of all ingredients being given a text box for manual input, we now allow selection of certain well-known types (at the moment, recipes and event tags) via drop-down. We also only provide the manual input box for ingredients that can be converted *from* the text box's string value, and if that's not possible, we default to requiring something out of the Stash. This improves the Tester UI safety somewhat and makes ingredient picking harder to accidentally break.
Add UI for manually creating event tags. The "Known Events" project tree item can now be used to create a card that edits all known event tags. This allows us to remove some hacky stuff from the WPF Seed library too, since a SwitchBoard project can do everything it needs to wire up working events to UI widgets now.
Trivial cleanup
Minor refactor/cleanup to Deserializer
Simplify widgets a tiny bit for Event Tag List editor UI.
Refactoring and cleanup for drag/drop code.
Refactor some UI generation code. This moves the "UI extension helper" concept into a proper interface, and places the actual "generated UI hosting" functionality into a proper widget. As a starting point, this preserves the overall "draggable cards on a canvas" experience of the UI. However, this also opens the door to clean ways to do things like have tabbed document support, document tabs that use grid layouts instead of the canvas/card metaphor, and so on. Overall this also cleans up a lot of small quibbles about the way the UI generation code interacted with its containers. There is plenty more room for improvement in the UI code overall, though!
Assorted exploration into UI refinements. Adds some tabs to the main window UI in anticipation of tabbed document editing. Also adds tabs for the Quick Stash as well as a new Log display tab. Test items are no longer seeded into the Quick Stash on startup. When recipe usage reports are generated, they are now routed to new cards on the current workspace as well as the logs tab; this is just to see how I like it and if it sticks. Language around the Quick Stash has been clarified, in anticipation of having more than one stash available during use. Fixes a bug where proto-steps (i.e. with no specific action chosen yet) couldn't be deleted properly. Recipe-use reports now include timestamp and elapsed time information to make the logs more useful.
Turn omitted ingredients into nulls when using the Reflection consent facilitator. This is a bit of a temporary hack for now, but it helps get around the case where parameters are genuinely optional. It looks like Reflection may not retain the code-time annotations for reference types, leaving the base assumption that everything is nullable; so there might not be much getting around this for Reflection itself. The overall nullability/optionality of ingredients still needs a proper implementation, so I'll probably revisit all this then.
Add some UI seeds for images and List View controls.
Fix a bug that made manually-created recipes uneditable when first loading up a saved project.
Fix up some issues with product mappings. We were previously doing some sloppy things, two of which were beginning to interact in weird ways, and both of which are now removed. Unmapped products are no longer relayed back from PerformStep when giving consent to a recipe. This used to mask a problem where inner consent steps (e.g. when mapping nested recipe uses) did not relay their products at all; this is addressed using a hidden "echo products" consent mapping, which just copies everything from the step out, as expected. We used to only allow a product to be mapped once, and discarded it if it was mapped, but let it get passed along if it wasn't mapped. (Yes this is confusing.) As a nice benefit of cleaning up this oddity, products can now be mapped more than once by the same consent step, which is just plain useful. Since the .Net language ecosystem mostly just believes in singular return values from functions, we can stick with just mapping products in a hard-coded fashion (eventually, things like "out" params can be handled by just traversing the parameter list in order).
Hack - remove surrounding quotes from file names when loading images. This is mostly just to make things a tiny bit more convenient for me, since I really like the Ctrl+Right-Click / Copy as Path functionality, which for some unfathomable reason likes to quote the copied filename.
Throw some hacks into the UI seeds. This gets some of the overall layout effects I want for my current test case (a sort of image-gallery setup) in a very, very awful way... but it works. The benefit of doing this is that I can go back later and turn each of these into more "proper" implementations, once the system is a little more fleshed out - and this helps highlight a few places that need direct attention soon.
Starting some C++ interop support. This change adds some adapter logic that allows C++ std::algorithm functions to work on C# containers. For now the selection is limited (std::rotate and List<>) but this illustrates how much more can be added over time. By implementing an adapter-iterator in C++, and using a C# delegate to do the actual "heavy lifting" of operating on the container, we can write trivial wrappers in C# that effectively make std::algorithm available to any SwitchBoard code. At some point, we'll be able to direclty invoke these from recipes too, meaning that std::algorithm will be available to anything using the Recipe model!
First rough implementation of draggable list items. This adds a feature to the "editable list" widget where each item can be dragged into a new position in the list. At the moment, the visual side of this is quite crude and limited, but the underlying functionality (i.e. actually reordering stuff in the data) works, and that gives us a great place to grow from.
Refine the look and feel of dragging around list items. This adds a border to the item being moved, and allows smoothly moving the item through the list as much as desired (instead of the previous one-at-a-time clunkiness).
Rough support for manually providing ingredients other than text. This basically adds a type conversion mechanism so that anything that we know how to convert from a string could be used as manual input in the Recipe Tester interface. (Yes this means we could wire up things like custom conversion recipes or even deserializers here eventually... and that is in fact the plan! Just, not today.)
Temporary hack to mess with property access. Since property getters and setters are methods in .Net, we can already see them when we use Reflection to generate a Cookbook of Recipes from a C# class. This means we can do things like directly set control widths and so on from a recipe already - allowing an existing hack to be removed from the WPF Seeds code. However, the current experience of adding cookbooks from known (complicated) types is a bit messy, so this will need a lot of refinement in the near future.
Initial web browser test using CEFSharp. This adds CEFSharp as an embedded browser framework, via a new optional module called WebSeeds. The WebSeeds module can be used alongside the UISeeds to drop a browser control onto a WPF container, and navigate it to various URLs. There's a long way to go before this does much useful, but it's a wonderful starting point for getting SwitchBoard interacting with additional ecosystems like HTML/JS.
Project tree: add list of known Data Categories, and alphabetize items.
Don't let tall cards fall off the top of the workspace. This is just a minor QoL tweak to the UI so that really large cards (e.g. cookbooks generated from very complex classes) don't get so tall that the "center in workspace" logic makes the top of them unreachable - since that also means we can't drag them around the workspace anymore.
Add ability to right-click Data Categories in the project tree and generate cookbooks from them. This also replaces the fixed hack in the main window's menus that did this for a hard-coded type.
Add support for enumeration-style data categories. A "Fixed Set" data category offers a limited number of pre-defined values. These can be optionally named, as in the "enum" facility of many languages, or anonymous. This change includes the ability to generate the appropriate Data Category data when using Reflection to create recipes or inspect types. This also adds some UI support for selecting items from a fixed set of possible values when using recipes that have such ingredients.
Add context menu to Stash items and allow them to be discarded.
.and then it was now.
To date, SwitchBoard has had 198 commits, across just under three months of work. There are nearly 10,000 words written across all those commit messages - a fairly hefty bit of writing in and of itself!
And this is why I put effort into keeping a detailed commit history - because in the all-too-frequent moments when I think I'm not "getting enough done," I can just pop open the records and prove those feelings very, very wrong.
The best part, to me?
I'm barely getting started.