The library
This section gives an introduction to Small Windows. The first part of a Small Windows application is the MainWindow
function. It corresponds to main
in regular C++. Its task is to set the name of the application and create the main window of the application.
In this book we talk about definitions and declarations. A declaration is just a notification for the compiler, while a definition is what defines the feature. Below is the declaration of the MainWindow
function. Its definition is left to the user of Small Windows.
void MainWindow(vector<String>argumentList, SmallWindows::WindowShow windowShow);
Simply put, in Windows the application does not take any initiative; rather, it waits for messages and reacts when it receives them. Informally speaking, you do not call Windows, Windows calls you.
The most central part of Small Windows is the Application
class. In Windows, each event generates a message that is sent to the window that has input focus at the moment. The Application
class implements the RunMessageLoop
method, which makes sure that each message is sent to the correct window. It also closes the application when a special quit message is sent.
The creation of a window takes place in two steps. In the first step, the RegisterWindowClasses
method sets features such as style, color, and appearance. Note that Windows classes are not C++ classes:
class Application { public: static int RunMessageLoop(); static void RegisterWindowClasses(HINSTANCE instanceHandle); };
The next step is to create an individual window, which is done by the Window
class. All virtual
methods are empty and are intended to be overridden by sub classes shown as follows:
class Window { public:
A window can be visible or invisible, enabled or disabled. When a window is enabled, it accepts mouse, touch, and keyboard input:
void ShowWindow(bool visible); void EnableWindow(bool enable);
The OnMove
and the OnSize
methods are called when the window is moved or resized. The OnHelp
method is called when the user presses the F1 key or the Help button in a message box:
virtual void OnMove(Point topLeft); virtual void OnSize(Size windowSize); virtual void OnHelp();
The client area is the part of the window that it is possible to paint in. Informally, the client area is the window minus its frame. The contents of the client area can be zoomed. The default zoom factor is 1.0:
double GetZoom() const; void SetZoom(double zoom);
The timer can be set to an interval in milliseconds. The OnTimer
method is called on every interval. It is possible to set up several timers, as long as they have different identity numbers:
void SetTimer(int timerId, unsigned int interval); void DropTimer(int timerId); virtual void OnTimer(int timerId);
The OnMouseDown
, OnMouseUp
, and OnDoubleClick
methods are called when the user presses, releases, or double-clicks on a mouse button. The OnMouseMove
method is called when the user moves the mouse with at least one button pressed. The OnMouseWheel
method is called when the user moves the mouse wheel with one click:
virtual void OnMouseDown(MouseButton mouseButtons, Point mousePoint, bool shiftPressed, bool controlPressed); virtual void OnMouseUp(MouseButton mouseButtons, Point mousePoint, bool shiftPressed, bool controlPressed); virtual void OnDoubleClick(MouseButton mouseButtons, Point mousePoint, bool shiftPressed, bool controlPressed); virtual void OnMouseMove(MouseButton mouseButtons, Point mousePoint, bool shiftPressed, bool controlPressed); virtual void OnMouseWheel(WheelDirection direction, bool shiftPressed, bool controlPressed);
The OnTouchDown
, OnTouchMove
, and OnTouchDown
methods work in the same way as the mouse methods. However, as the user can touch several points at the same time, the methods takes lists of points rather than an individual point:
virtual void OnTouchDown(vector<Point> pointList); virtual void OnTouchMove(vector<Point> pointList); virtual void OnTouchUp(vector<Point> pointList);
The OnKeyDown
and OnKeyUp
methods are called when the user presses or releases a key. If the user presses a graphical key (a key with an ASCII value between 32 and 127, inclusive), the OnChar
method is called in between:
virtual bool OnKeyDown(WORD key, bool shiftPressed, bool controlPressed); virtual void OnChar(TCHAR tChar); virtual bool OnKeyUp(WORD key, bool shiftPressed, bool controlPressed);
The Invalidate
method marks a part of the client area (or the whole client area) to be repainted; the area becomes invalidated. The area is cleared before the painting if clear
is true
. The UpdateWindow
method forces a repainting of the invalidated area. It causes the OnPaint
method to be called eventually:
void Invalidate(Rect areaRect, bool clear = true) const; void Invalidate(bool clear = true) const; void UpdateWindow();
The OnPaint
method is called when some part of the client area needs to be repainted and the OnPrint
method is called when it is sent to a printer. Their default behavior is to call the OnDraw
method with Paint
or Print
as the value of the drawMode
parameter:
virtual void OnPaint(Graphics& graphics) const; virtual void OnPrint(Graphics& graphics, int page, int copy, int totalPages) const; virtual void OnDraw(Graphics& graphics, DrawMode drawMode) const;
The OnClose
method closes the window if TryClose
returns true
. The OnDestroy
method is called when the window is being closed:
virtual void OnClose(); virtual bool TryClose(); virtual void OnDestroy();
The following methods inspect and modify the size and position of the window. Note that we cannot set the size of the client area; it can only be set indirectly by resizing the window:
Size GetWindowSize() const; void SetWindowSize(Size windowSize); Point GetWindowPosition() const; void SetWindowPosition(Point topLeft); Size GetClientSize() const;
In the word processor and spreadsheet programs in this book, we handle text and need to calculate the size of individual characters. The following methods calculate the width of a character with a given font. They also calculate the height, ascent, and average character width of a font:
int GetCharacterWidth(Font font, TCHAR tChar) const; int GetCharacterHeight(Font font) const; int GetCharacterAscent(Font font) const; int GetCharacterAverageWidth(Font font) const;
The ascent line separates the upper and lower part of a letter, shown as follows:
Finally, the MessageBox
method displays a simple message box in the window:
Answer MessageBox(String message, String caption = TEXT("Error"), ButtonGroup buttonGroup = Ok, Icon icon = NoIcon, bool help = false) const; };
The Window
class also uses the Graphics
class responsible for drawing text and geometrical objects in the window. A reference to a Graphics
object is sent to the OnPaint
, OnPrint
, and OnDraw
methods in the Window
class. It can be used to draw lines, rectangles, and ellipses and to write text:
class Graphics { public: void DrawLine(Point startPoint, Point endPoint, Color penColor, PenStyle penStyle = Solid); void DrawRectangle(Rect rect, Color penColor, PenStyle = Solid); void FillRectangle(Rect rect, Color penColor, Color brushColor, PenStyle penStyle=Solid); void DrawEllipse(Rect rect, Color penColor, PenStyle = Solid); void FillEllipse(Rect rect, Color penColor, Color brushColor, PenStyle penStyle=Solid); void DrawText(Rect areaRect, String text, Font font, Color textColor, Color backColor, bool pointsToMeters = true); };
The Document
class extends the Window
class with some functionality common to document-based applications. The scroll thumbs are automatically set to reflect the visible part of the document. The mouse wheel moves the vertical scroll bar one line-height for each click. The height of a line is set by the constructor. The code snippet for it is shown as follows:
class Document : public Window { public:
The dirty flag is true
when the user has made a change in the document and it needs to be saved. In Document
, the dirty flag is set manually, but in the following StandardDocument
subclass it is handled by the framework:
bool IsDirty() const; void SetDirty(bool dirty);
The caret is the blinking marker that indicates to the user where they should input the next character. The keyboard can be set (with the Insert key) to insert or overwrite mode. The caret is often a thin vertical bar in insert mode and a block with the width of an average character in overwrite mode.
The caret can be set or cleared. For instance, in the word processor, the caret is visible when the user writes text and invisible when the user marks text. When the window gains focus, the caret becomes visible if it has earlier been set. When the window loses focus, the caret becomes invisible, regardless of whether it has earlier been set:
void SetCaret(Rect caretRect); void ClearCaret(); void OnGainFocus(); void OnLoseFocus();
A document may hold a menu bar, which is set by the SetMenuBar
method:
void SetMenuBar(Menu& menuBar);
The OnDropFiles
method is called when the user drops one or several files in the window. Their paths are stored in the path list:
virtual void OnDropFile(vector<String> pathList);
The keyboard mode of a document can be set to insert or overwrite as follows:
KeyboardMode GetKeyboardMode() const; void SetKeyboardMode(KeyboardMode mode);
The OnHorizontalScroll
and OnVerticalScroll
methods are called when the user scrolls the bar by clicking on the scroll bar arrows or the scroll bar fields, or dragging the scroll thumbs. The code snippet for it is shown as follows:
virtual void OnHorizontalScroll(WORD flags,WORD thumbPos=0); virtual void OnVerticalScroll(WORD flags, WORD thumbPos =0);
There is a large set of methods for inspecting or changing scroll bar settings. The size of a line or page is set by the constructor:
void SetHorizontalScrollPosition(int scrollPos); int GetHorizontalScrollPosition() const; void SetVerticalScrollPosition(int scrollPos); int GetVerticalScrollPosition() const; void SetHorizontalScrollLineWidth(int lineWidth); int GetHorizontalScrollLineHeight() const; void SetVerticalScrollLineHeight(int lineHeight); int GetVerticalScrollLineHeight() const; void SetHorizontalScrollPageWidth(int pageWidth); int GetHorizontalScrollPageWidth() const; void SetVerticalScrollPageHeight(int pageHeight); int GetVerticalScrollPageHeight() const; void SetHorizontalScrollTotalWidth(int scrollWidth); int GetHorizontalScrollTotalWidth() const; void SetVerticalScrollTotalHeight(int scrollHeight); int GetVerticalScrollTotalHeight() const; };
The Menu
class handles the menu bar, a menu, a menu item, or a menu item separator (a horizontal bar) in the document. The selection
listener is called when the user selects the menu item. The enable
, check
, and radio
listeners are called (unless they are null) when the item is about to become visible. If they return true
, the item is enabled or annotated with a check box or radio button:
class Menu { public: void AddMenu(Menu& menu); void AddSeparator(); void AddItem(String text, VoidListener selection, BoolListener enable = nullptr, BoolListener check = nullptr, BoolListener radio = nullptr); };
An accelerator is a shortcut command. For instance, often, the Open item in the File menu is annotated with the text Ctrl+O. This means that you can obtain the same result by pressing the Ctrl key and the O key at the same time, just as if you selected the Open menu item. In both cases, the Open dialog is displayed.
The Accelerator
class holds only the TextToAccelerator
method. It interprets the menu item text and adds the accelerator, if present, to the accelerator set:
class Accelerator { public: static void TextToAccelerator(String& text, int idemId, list<ACCEL>& acceleratorSet); };
The StandardDocument
class extends the Document
class and sets up a framework that takes care of all traditional tasks, such as load and save, and cut, copy, and paste, in a document-based application:
class StandardDocument : public Document { public:
The StandardDocument
class comes equipped with the common File, Edit, and Help menus. The File menu can optionally (if the print
parameter is true
) be equipped with menu items for printing and print previewing:
Menu StandardFileMenu(bool print); Menu StandardEditMenu(); Menu StandardHelpMenu();
The ClearDocument
method is called when the user selects the New menu item; its task is to clear the document. The WriteDocumentToStream
method is called when the user selects the Save or Save As menu item and the ReadDocumentFromStream
method is called when the user selects the Open menu item:
virtual void ClearDocument(); virtual bool WriteDocumentToStream(String name, ostream& outStream)const; virtual bool ReadDocumentFromStream(String name, istream& inStream);
The CopyAscii
, CopyUnicode
, and CopyGeneric
methods are called when the user selects the Cut or Copy menu item and the corresponding ready
method returns true
. The code snippet for it is shown as follows:
virtual void CopyAscii(vector<String>& textList) const; virtual bool IsCopyAsciiReady() const; virtual void CopyUnicode(vector<String>& textList) const; virtual bool IsCopyUnicodeReady() const; virtual void CopyGeneric(int format, InfoList& infoList) const; virtual bool IsCopyGenericReady(int format) const;
In the same way, the PasteAscii
, PasteUnicode
, and PasteGeneric
methods are called when the user selects the Paste menu item and the corresponding ready
method returns true
:
virtual void PasteAscii(const vector<String>& textList); virtual bool IsPasteAsciiReady (const vector<String>& textList) const; virtual void PasteUnicode(const vector<String>& textList); virtual bool IsPasteUnicodeReady (const vector<String>& textList) const; virtual void PasteGeneric(int format, InfoList& infoList); virtual bool IsPasteGenericReady(int format, InfoList& infoList) const;
The OnDropFile
method checks the path list and accepts the drop if exactly one file has the suffix of the document type of the application (set by the constructor):
void OnDropFile(vector<String> pathList); };
In Small Windows, we do not care about the pixel size. Instead, we use logical units that stay the same, regardless of the physical resolution of the screen. We can choose from the following three coordinate systems:
LogicalWithScroll
: A logical unit is one hundredth of a millimeter, with the current scroll bar settings taken into account. The drawing program and word processor use this system.LogicalWithoutScroll
: A logical unit is one hundredth of a millimeter also in this case, but the current scroll bar settings are ignored. The spreadsheet program uses this system.PreviewCoordinate
: The client area of the window is set to a fixed logical size when the window is created. This means that the size of the logical units changes when the user changes the window size. The Tetris game and thePreviewDocument
class uses this system.
Besides the StandardDocument
class, there is also the PrintPreviewDocument
, which class that also extends the Document
class. It displays one of the pages of a standard document. It is possible for the user to change the page by using the arrow keys and the
Page Up
and
Page Down
keys or by using the vertical scroll bar:
class PrintPreviewDocument : Document { public: PrintPreviewDocument(StandardDocument* parentDocument, int page = 1, Size pageSize = USLetterPortrait); bool OnKeyDown(WORD key, bool shiftPressed, bool controlPressed); void OnVerticalScroll(WORD flags, WORD thumbPos = 0); void OnPaint(Graphics& graphics) const; };
There are also the simple auxiliary classes:
Point
: It holds a two-dimensional point (x and y)Size
: It holds two-dimensional width and heightRect
: It holds the four corners of a rectangleDynamicList
: It holds a dynamic listTree
: It holds a tree structureInfoList
: It holds a list of generic information that can be transformed into a memory block
The Registry
class holds an interface to the Windows Registry, the database in the Windows system that we can use to store values in between the execution of our applications. The Clipboard
class holds an interface to the Windows Clipboard, an area in Windows intended for short-term data storage that we can use to store information cut, copied, and pasted between applications.
The Dialog
class is designed for customized dialogs. The Control
class is the root class for the controls of the dialog. The CheckBox
, RadioButton
, PushButton
, ListBox
, and ComboBox
classes are classes for the specific controls. The TextField
class holds a text field that can be translated to different types by the Converter
class. Finally, the PageSetupDialog
class extends the Dialog
class and implements a dialog with controls and converters.