News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests
NB: Posting URL's See here: Posted URL Change

Main Menu

Designing a program: user interface & some technical issues

Started by NoCforMe, October 08, 2023, 11:50:00 AM

Previous topic - Next topic

NoCforMe

The motivation for this was a post by @Rockphorr about my editor, EdAsm. He made an excellent suggestion, which is to add a configuration editor. Currently it uses a configuration file to add items to the user interface, which works fine. But as they pointed out, adding or editing these items isn't necessarily easy: you have to know where to look for this stuff (program folder and .cfg file), and then you have to edit that file, which is just a text file but in a particular format for the editor's parser to consume it.

Even I, as the program's author, find that to be a pain in the ass.

So I'm working on a configuration editor. I've come up with the user-interface part of it, and thought I'd share the process I went through in designing it. And there are some technical issues (one gnarly one, anyhow) that need to be addressed, so I'll discuss that as well.

The User Interface
The task here isn't rocket science: each configuration item has two strings, one being the "display" text which shows in EdAsm's Inserts area, the other being the text that actually gets inserted. So we need to be able to edit those two strings for each item (or add them for new items). Not difficult: sounds like a job for a listbox. (I've devised a way to split a listbox to show two different pieces of text for each item; see screenshot below.)

So we have 3 functions here:
  • Add a new item
  • Edit an existing item
  • Delete an item

(You'll notice that I left off the button for that last operation, but you can just imagine it's there.)

Editing and deleting are easy-peasy: when the user selects an existing item by using the listbox, the strings get put into the two edit controls. The user types in the new text and clicks Update Item, or clicks Delete Item (just imagine it's there) to delete it.

But how to add a new item? I'm sorry to say that my first thought on this was a rather stupid one: I figured that after clicking Add New Item, I could blank out the edit controls, wait for the user to type new text into them, then click Update Item to add the new item.

Wrong! That would be the worst possible way to implement this function. Technically what it does is turn the dialog into a modal process, where by choosing a function it changes the meaning of the operation of the dialog. (This is one of the things that makes vi such a horrible piece of software to use.) Users hate this, and for good reason. So cancel that!

The obvious way to do this is via another dialog, shown below. Click Add New Item and the dialog comes up; type into it, click OK, and the new item appears in the list. The added step of having another dialog is better than any alternative from the user's point of view.

Some Technical Issues
OK, so that's solved. But there's a quite difficult programming problem that this brings with it. Here's the problem:

We're dealing with a collection of text strings here, any of which can be any length. Let's say the user chooses to edit one of the items. They type new text into the edit control. Now we have to replace that text in whatever place it's stored.

If the new text is the same length or shorter than the original text, no problem: we can just copy it right back over the old text. But what if the new text is longer? You can see that we have a problem here ... (We could solve this in a semi-dumbass way by declaring that all strings will occupy a fixed amount of storage, say what we think will be the longest possible length of a line (which of course some user will exceed and get pissed off at!). But I can't see wasting all that memory just to simplify our storage scheme.)

A word about text storage: because we really have no idea just how much text we're going to have to deal with here, it seems the best method of storage is in a heap which we allocate dynamically, rather than using a static data area (either in .data or in .data?), since we might easily overrun the static area and then be up Shit Creek without a paddle.

What's needed is some kind of heap manager. Ugh; not a trivial thing. Now, I have written such code for other projects where I need to store lots of variable-length text strings. The one "manager" I wrote uses these functions:

  • GimmeHeap() allocates a chunk of memory (size in EAX) and takes a pointer that will be used for the allocation.
  • ReallocateHeapEntry() reallocates memory for an item that is now larger than before.

These routines keep the heap tightly packed and reorganize the heap to get rid of any empty space so no bytes are wasted.

They are a pain in the ass to code. When you change the length of a single item, you have to move everything below it in memory, and have changes ripple through the entire heap past that point. A lot of REP MOVSBs and such. It took me a long time to get them working reliably. So I could take these and maybe modify them a little to work with the config editor.

There are other technical issues: the whole process (configuration editing) would consist of
  • Reading the configuration file
  • Parsing it to extract the config strings
  • Storing the strings in the text heap
  • Letting the user edit the config items (and reorganizing the heap as they go)
  • Writing the config items back to the config file (this step's actually easy)
  • Resetting EdAsm's Insert items according to the config contents

Plus there's the added complication that EdAsm's config file actually has two sets of config items: one for "Inserts" which are shown on-screen, the other for "Goodies" which go into a selection dialog. This complicates the user interface. You could have two listboxes, one for Inserts, the other for Goodies, but that would be awkward (you can see how much space these take up). I'm thinking maybe putting these two things in a tab control might work, so the user can easily switch between the two.

Anyhow, you can see how seemingly simple tasks actually require a good deal of thought.

And of course if anyone has any brilliant suggestions here, please post them!
Assembly language programming should be fun. That's why I do it.

fearless

I would opt for something similar to Sniplets in RadASM. A treeview and a edit control. The treeview allows users to categorize the bits of information. The display name for the sniplets is the name of the text file used. 

The underlying directory structure and text files allow for recursing and loading the category nodes (folders) and the child item (text files). Any changes to the content are saved back to the appropriate text file. 

Having the content as text files, allows the user to add whatever information they require and allows them to edit it outside of the editor or to add content by copying pasting text files into folders - and/or renaming, adding folders to help categorize the content - which is picked up next time the editor is loaded.


NoCforMe

Mmmm, my first reaction to that scheme is that it's much too complex for my configuration needs. All my configuration items exist at just one level: there's no tree structure there at all. And I'm not sure how having text in different files would work.

It's basically a flat model, just an array of pairs of text strings. What you described is a good solution for data that's inherently tree-structured, which mine is not.
Assembly language programming should be fun. That's why I do it.

fearless

A listview might be the way to go then. Its text items are limited to about 260 chars from memory, but it does show a tooltip of the content when the column of the text is too small to show it all if one of the styles INFOTIP i think is set.

If the content is likely to every grow to a large amount, then a treeview contol would help organize and categorize the content. If the content is only likely to be say max 20/30 items then a listview or listbox may suffice.

I would still keep the content in text files, but an alternative is to use an ini file and read the entry. Might have to do some juggling to allow multiline, like place /r/n in the ini entry and when loading into memory replace the /r/n with 13,10 for carriage return/newline. Also maybe handle tabs with /t

Something like the following as an example:

[Stuff]
CreateWindow=INVOKE CreateWindowEx,/r/n/tExstyles,/t;EXstyles/r/n/tclassName,/t;class name/r/n/twindowName,/t;window title/text/r/n/tstyles,/t;styles/r/n/tx,/t;X-pos/r/n/ty,/t;Y-pos/r/n/twidth,/t;width/r/n/theight,/t;height/r/n/tparent,/t;parent/r/n/tmenu,/t;menu/ID/r/n/tinstance,/t;instance handle/r/n/tparam/t;param/r/n



NoCforMe

The configuration files have carriage returns/line feeds and tabs embedded in them, and that works fine. (The only thing that needs massaging are embedded double quotes, which must be doubled so they will be read correctly by the parser.)

Did you even look at my screenshots? The config strings won't fit into a listbox entry, but it doesn't matter: enough of it is shown so the user knows what the entry is, The text is shown in its entirety in the edit control when selected.

A listview would be overkill for this application. Actually, I hate listviews because of the brain-damaged way they were implemented by Micro$oft. All that item and subitem nonsense.
Assembly language programming should be fun. That's why I do it.