Modeless Dialogs


A modeless dialog differs from a modal dialog in that it runs alongside the main window of the application. That is, the main window remains enabled whilst the modeless dialog is displayed. Conversely, modal dialogs are used for immediate input; where, until the dialog is dismissed, other parts of the application are disabled.

Modeless dialogs usually include a title bar and a system menu. The style statement for a modeless dialog usually looks something like the one shown below.

STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE

If the style WS_VISIBLE is not included, the dialog needs to be made visible manually (via the function show_window). Modeless dialogs are destroyed via the function destroy_window.

Colors Revisited

In chapter 7, a color manipulation program was presented. That program contained 10 child windows and the program was one of the more complex examples of the book. This section presents a similar program, but uses a modeless dialog to perform the same task. When a modeless dialog is used, the program is much simpler. A snapshot of colors mach 2 is shown below.

The application consists of:

The modeless dialog contains an array of three color declared within the dialog data. This array of three integers holds the currently selected red, green, blue color components. During initialization of the dialog, the scroll bars are initialized to have a range of 0 through to 255 - as shown below.

case message::initialize_dialog:
    {
        dialog_data* data = new dialog_data();
        set_window_pointer(dialog_handle, offset::user_data, (void*)data);

        for (int control_identity = 10; control_identity < 13; control_identity++)
        {
            handle control_handle = get_child_window(dialog_handle, control_identity);
            set_scroll_range(control_handle, scrollbar_identity::control, 0, 255, false);
            set_scroll_position(control_handle, scrollbar_identity::control, 0, false);
        }
    }
    return (result)true;

The intial color is black, so that each of the scroll bars is positioned at zero. Note that the identities of the scroll bar controls are hard-coded into the program as 10, 11 and 12.

The notification message::vertical_scroll is where most of the action is. This notification controls the scroll bars and sets the background brush of the owner. The code for this notification is shown below.

case message::vertical_scroll:
    {
        dialog_data* data = (dialog_data*)get_window_pointer(dialog_handle, offset::user_data);

        handle control_handle = (handle)parameter2;
        int control_identity = get_window_integer(control_handle, offset::identity);
        int index = control_identity - 10;

        switch (low_part(parameter1))
        {
        case scrollbar_notify::page_down:
            data->color_array[index] += 15;        // fall through
        case scrollbar_notify::line_down:
            data->color_array[index] = minimum(255, data->color_array[index] + 1);
            break;

        case scrollbar_notify::page_up:
            data->color_array[index] -= 15;        // fall through
        case scrollbar_notify::line_up:
            data->color_array[index] = maximum(0, data->color_array[index] - 1);
            break;

        case scrollbar_notify::top:
            data->color_array[index] = 0;
            break;

        case scrollbar_notify::bottom:
            data->color_array[index] = 255;
            break;

        case scrollbar_notify::slider_position:
        case scrollbar_notify::slider_track:
            data->color_array[index] = high_part(parameter1);
            break;

        default:
            return (result)false;
        }

        set_scroll_position(control_handle, scrollbar_identity::control, data->color_array[index], true);
        set_child_integer(dialog_handle, control_identity + 3, data->color_array[index], false);

        handle parent = get_parent(dialog_handle);

        delete_object((handle)set_class_pointer(parent,
            class_offset::background_brush,
            create_solid_brush(red_green_blue(data->color_array[0], data->color_array[1], data->color_array[2]))));

        invalidate_rectangle(parent, (const irectangle*)null, true);
    }
    return (result)true;

after the scroll position has been updated, a new brush is created and set as the brush of the parent window.

Adjusting the Message Loop

The main message loop must be adjusted for each individual modeless dialog. This restricts where modeless dialogs may be created. For the case at hand, the code to create the dialog is shown below.

extern "C" int __stdcall WinMain(handle module_handle,
                                 handle reserved,
                                 character* command_line,
                                 int show_command)
{
 windows_class<character>;

 wclass.style     = class_style::horizointal_redraw | class_style::vertical_redraw;
 wclass.procedure = client;
 wclass.Extra     = 0;
 wclass.window    = 0;
 wclass.module    = module_handle;
 wclass.icon      = load_icon((handle)0,(const character*)icon_identity::application);
 wclass.cursor    = load_cursor((handle)0,(const character*)cursor_identity::arrow);
 wclass.brush     = get_standard_object(standard_brush::White);
 wclass.name      = name;

 atom atom_name = register_class(&wclass);

 character title[255];
 load_string(module_handle,FrameIdentity,title,255);

 handle window_handle = create_window((const character*)atom_name,
                                      title,
                                      style::standard,
                                      use_default,
                                      use_default,
                                      use_default,
                                      use_default,
                                      (handle)0,
                                      (void*)0);


 show_window(window_handle,show_command);
 update_window(window_handle);

 handle dialog_handle = create_dialog(module_handle,
                                      (const character*)color_dialog,
                                       window_handle,
                                       color_procedure);
 
 queue queue_message;
 while (get_message(&queue_message))
  {
   if (!is_dialog_message(dialog_handle,&queue_message))
    {
     translate_message(&queue_message);
     dispatch_message(&queue_message);
    }
  }

 return queue_message.parameter1;
}

The colors dialog is created just after the client window is created. Then a message loop of a different type is entered - as shown below.

queue queue_message;
while (get_message(&queue_message))
 {
  if (!is_dialog_message(dialog_handle,&queue_message))
   {
    translate_message(&queue_message);
    dispatch_message(&queue_message);
   }
 }

The function is_dialog_message must be called for each message to ascertain whether the message is directed to the modeless dialog. The main message loop is adjusted for individual dialogs (as well as for accelerators) - neither of which should be specifically mentioned in the message loop (in the ideal world that is). The function sends the message to the dialog if it was meant for that dialog; otherwise, the message is translated and dispatched as usual. This allows for the dialog keyboard interface to be invoked for the modeless dialog.