This is the third article in a series. To jump to the other articles, follow these links. [Lesson 1 | Lesson 2 | Lesson 3 | Lesson 4]

Lesson 3

  • Panes
  • Cut, copy, paste
  • Child control
  • Creating splitters
  • Complex layouts with splitters

Download project files

Up to this point we've set up a basic window with some controls for the game. We have a map, an input control, output, and two grids. This is a great start, but we can do more. In BYOND 3.0 you could drag a splitter around to make the output control bigger or smaller. Wouldn't it be nice to do that here? Let's explore what it takes to use splitters, and for that, we need to talk about panes.

A pane is a window in the skin that only ever shows up inside other windows or panes. To create one, go back to the main skin editor and create a new window. Go to the window's properties and into the General tab. Let's call this window mappane. Now go to the Options pane. Give it a title (just "Map" will do), and then check the box that says Is a pane (not a main window).

Now let's fill this pane with controls. Remember the map and input controls from the first lesson? We'll recreate them here. Actually, we'll move them here. Press the Done button on the editor panel, and then open up mainwindow again. Select both the map and the input control. Hit Ctrl+X on the keyboard to cut them out of this window, which will also put them in a special clipboard. (It's not the Windows clipboard, though, so you can't paste into another instance of DM.) Now close this window, and go back to the pane we just made. Once that's open, press Ctrl+V to paste the map and input controls here.

Because the map and input controls will expand to fit the pane—because we gave them anchors before—we should make them fill the whole pane. Move the input bar to the bottom of the pane, and stretch it out so it covers the full width. Now join the map and the input controls with the layout toolbar, just like before, but make sure the input control is the dominant control so only the map moves. Then resize the map to make it fill the rest of the pane. Finally, if you've changed the anchors at all, be sure to change them back so the map is anchored at top left and bottom right, and input is anchored to bottom left and bottom right.

We've just created a simple pane. Now it's time to use it.

Go back to the main window. There's a big blank space where the map and input controls were. We'll be filling it with something to hold the pane we made. Go to the editor panel and click the Child button, and then click in the window. A child control has been created. The whole purpose of the child control is to hold panes. Move the control into the same position and size that the map and input controls used to occupy. For now, give it anchors at the top left and bottom right.

So we've created the child control, but so far it hasn't been told to do anything. Here's where we use the pane. Go to the child control's properties and open the Options tab. In the box that says Left/top window ID, type in mappane. Press OK. Once this is done, you're not going to see a change. The window editor doesn't actually show you the pane itself, so to see how this works, you'll have to compile and test the project.

Let's test this now and see if everything's working the way we hope. Return to the skin editor, and compile. Now run the game and take a look. The map and input controls should appear where they always did.

But what's the point of all this? That will become clear when we make some more panes. So now, let's make two more panes. Create gridpane and outputpane. As you might have guessed, the grid and output controls we made earlier will be moved here. Go to mainwindow and select the output control, Ctrl+X to cut it, then go to outputpane and Ctrl+V to paste it there. The output control should fill its entire pane, so go to its properties and open the General tab, and press the Fill Window button. Anchor it to the top left and bottom right. Then go back to mainwindow, select both of the grids, then do the same cut and paste into gridpane. The inventory grid should be anchored to top left and 100,50, and equipment anchored to 0,50 and bottom right. Together they should fill up the entire pane. To make them even, use the layout toolbar to Distribute height evenly. At this point, you should also give both panes titles: "Grids" and "Output".

Back in mainwindow, we need to replace the controls we moved with new Child controls. We'll put one on the left, and anchor it to the top left and bottom left, and then another on the right, anchored to the top right and bottom right. Like with the other child control, set the Left/top window ID to gridpane for one, and outputpane for the other. This will look like a whole lot of nothing now, so again it's time to compile the project and test it again. Once you're sure everything looks good, let's continue.

Now, it's time to explore how to use splitters. A splitter is built into the child control, whenever you have both a left/top pane and a right/bottom pane. Let's say we want two vertical splitters: One between the grids and the map, and another between the map and the output. Since a child can only have a left and right, not three sections, we'll need another pane.

Call this new pane leftpane. Unoriginal, I know. In this pane we're going to create a child control, and have it fill the whole pane. Go to the General tab in its properties, and name it split1. Anchor it to the top left and bottom right. Now open its properties and go to the Options tab. Set the Left/top window ID to gridpane, and Right/bottom window ID to mappane. There's also a checkbox labeled Vertical splitter. We want a vertical splitter here, so make sure that box is checked. There's another box that says Splitter (% for left/top pane). Change the number in that box from 50 to 33.33. We want the grids to take up roughly 1/3 of this pane by default.

Now go back to the main window. Delete everything except one of the child controls. Stretch that out to fill the whole window, and anchor it to the top left and bottom left. Go to the General tab in its properties and change its name to split2. While still looikng at this child control's properties, open the Options tab. Set the Left/top window ID to leftpane that we just created. Set Right/bottom window ID to outputpane. Again, make sure the Vertical splitter box is checked. In the Splitter % box, enter the number 75. The leftpane will take up the leftmost 3/4 of the window.

Save your work and compile again. Open the game and you'll see two vertical splitters. Move the splitter on the left, and the grids and map will resize accordingly. If you move the one on the right, the one on the left will move a little bit as well. This is one of the trade-offs we make, but it's easy enough to readjust everything the way you want.

There's one last step, and now it seems pretty clear. There are two groups of controls with no splitters between them: The map and input controls, and the two grids. The input control obviously doesn't need changing here, but we might want to adjust the size of the two grids. Why not put a horizontal splitter there? This step requires adding two new panes, inventorypane and equipmentpane. By now we've covered enough that you should be able to move the grids to their new panes and re-anchor them without help. Give each pane a title: "Inventory" and "Equipment". Once they've been moved, add a new child control to gridpane; we'll call it gridsplit. Set inventorypane as the left/top pane, equipmentpane as the right/bottom, and we'll stick with a horizontal splitter at 50%. Compile again to see the changes.

So let's look back. We've created some panes and learned how to use them in the child control, which lets us use splitters. We now have an interface that has six panes (mappane, outputpane, gridpane, leftpane, inventorypane, and equipmentpane) and three splitters. The most important thing you can take away from this lesson is that for a flexible interface, many controls should be put in their very own pane. In fact so far the only place where two controls appear in a window or a pane is that the map and input controls are grouped together. Everything else can be moved separately. Later on we'll encounter some more exceptions to this, like if we create a rack of buttons, but we're still going to end up with a lot of panes.

Panes are good! But wait, why did we bother giving them titles if they don't have titlebars? Lesson 4 awaits.


  1. Change one of the vertical splitters to a horizontal splitter and test the result.
  2. When testing your project, try this command: .winset ";"
  3. Panes can be swapped around, which is something we'll explore more later. Right now you can see how it works by creating a test verb in the project.

    if(winget(usr, "mainwindow.split2", "right") == "outputpane")
    winset(usr, "mainwindow.split2", "right=gridpane")
    winset(usr, "leftpane.split1", "left=outputpane")
    winset(usr, "mainwindow.split2", "right=outputpane")
    winset(usr, "leftpane.split1", "left=gridpane")
  4. Try using winset() to change a splitter's position on the fly.

    winset(usr, "mainwindow.split2", "splitter=75")
    winset(usr, "leftpane.split1", "splitter=33.33")
    winset(usr, "gridpane.gridsplit", "splitter=50")
This looks good! The skin lessons are very informative, helped out a lot. Thank you for doing this.
Yes, they are very helpful. I always wanted to make my own skin and now I can thanks to you!
I'm having a little trouble one ONE particular part... and it should be the easiest part in the guide. The 'copy/paste the mainwindow' part. I cannot seem to get what you said done. And I have been here for hours rereading what you said over and over trying to understand it better each time and doing it. But, as much as I tried, I couldn't get it right. I'm not the type to continue if I can't get a part right. I must complete this step you set as a task to continue. So if you/anyone can help me understand it better I would appreciate it.

Hey, awesome tutorial here, I've learned a ton. Although, you forgot to tell us to set leftpane's option to "Is a pane", which if not checked makes it it's own window. I spent 30 minutes trying to figure out how to fix this, only to find that box needed checked, lol.

Or was that a test?