We'll use a DialogDirector to implement the font dialog box shown in the Motivation. The abstract class DialogDirector defines the interface for directors.
Widget is the abstract base class for widgets. A widget knows its director.class DialogDirector { public: virtual ~DialogDirector(); virtual void ShowDialog(); virtual void WidgetChanged(Widget*) = 0; protected: DialogDirector(); virtual void CreateWidgets() = 0; };
Changed calls the director's WidgetChanged operation. Widgets call WidgetChanged on their director to inform it of a significant event.class Widget { public: Widget(DialogDirector*); virtual void Changed(); virtual void HandleMouse(MouseEvent& event); // ... private: DialogDirector* _director; };
Subclasses of DialogDirector override WidgetChanged to affect the appropriate widgets. The widget passes a reference to itself as an argument to WidgetChanged to let the director identify the widget that changed. DialogDirector subclasses redefine the CreateWidgets pure virtual to construct the widgets in the dialog.void Widget::Changed () { _director->WidgetChanged(this); }
The ListBox, EntryField, and Button are subclasses of Widget for specialized user interface elements. ListBox provides a GetSelection operation to get the current selection, and EntryField's SetText operation puts new text into the field.
Button is a simple widget that calls Changed whenever it's pressed. This gets done in its implementation of HandleMouse:class ListBox : public Widget { public: ListBox(DialogDirector*); virtual const char* GetSelection(); virtual void SetList(List * listItems); virtual void HandleMouse(MouseEvent& event); // ... }; class EntryField : public Widget { public: EntryField(DialogDirector*); virtual void SetText(const char* text); virtual const char* GetText(); virtual void HandleMouse(MouseEvent& event); // ... };
The FontDialogDirector class mediates between widgets in the dialog box. FontDialogDirector is a subclass of DialogDirector:class Button : public Widget { public: Button(DialogDirector*); virtual void SetText(const char* text); virtual void HandleMouse(MouseEvent& event); // ... }; void Button::HandleMouse (MouseEvent& event) { // ... Changed(); }
FontDialogDirector keeps track of the widgets it displays. It redefines CreateWidgets to create the widgets and initialize its references to them:class FontDialogDirector : public DialogDirector { public: FontDialogDirector(); virtual ~FontDialogDirector(); virtual void WidgetChanged(Widget*); protected: virtual void CreateWidgets(); private: Button* _ok; Button* _cancel; ListBox* _fontList; EntryField* _fontName; };
WidgetChanged ensures that the widgets work together properly:void FontDialogDirector::CreateWidgets () { _ok = new Button(this); _cancel = new Button(this); _fontList = new ListBox(this); _fontName = new EntryField(this); // fill the listBox with the available font names // assemble the widgets in the dialog }
The complexity of WidgetChanged increases proportionally with the complexity of the dialog. Large dialogs are undesirable for other reasons, of course, but mediator complexity might mitigate the pattern's benefits in other applications.void FontDialogDirector::WidgetChanged ( Widget* theChangedWidget ) { if (theChangedWidget == _fontList) { _fontName->SetText(_fontList->GetSelection()); } else if (theChangedWidget == _ok) { // apply font change and dismiss dialog // ... } else if (theChangedWidget == _cancel) { // dismiss dialog } }