Nano-X 程式設計, #4:設定 Window Manager(wm.c)

jollen 發表於 January 13, 2007 11:11 PM

wm.c image.c 為範本,wm.c 執行後在畫面上只會看到圖片圖(沒有視窗標題列與邊框),並且可使用滑鼠來拖曳圖片。本範例主要在展示以下的 Nano-X API 程式設計方法:

˙ 如何設定視窗屬性(window manager)。
˙ 如何與使用者做互動(human interactive)。

要寫出「托曳圖片」的功能,首先必須把視窗的標題列與邊框去除,否則只能在標題列上托曳整個「視窗」。做法非常簡單,只要在建立視窗時設定 window manager 即可:

   wid = GrNewWindowEx(GR_WM_PROPS_APPWINDOW | 
                       GR_WM_PROPS_NODECORATE | 
                       GR_WM_PROPS_NOAUTOMOVE,
                       NULL,
                       GR_ROOT_WINDOW_ID, 
                       0, 0, 
                       image_jollen.width, /* 圖片寬度 */
                       image_jollen.height /* 圖片高度 */, 
                       0xFFFFFF);

粗體字的地方是新加入的 window manager 屬性,為了達到我們的要求,我們指定了3個屬性值:

  • GR_WM_PROPS_APPWINDOW:使用 window manager 的外觀。
  • GR_WM_PROPS_NODECORATE:不要有視窗邊框。
  • GR_WM_PROPS_NOAUTOMOVE:第一次顯示視窗時不移動視窗。

由於我們並未指定 GR_WM_PROPS_CAPTION,因此不會有視窗標題列。 在處理使用者互動上,我們需要自行處理3個滑鼠事件:

  • GR_EVENT_MASK_MOUSE_POSITION:滑鼠移動事件。
  • GR_EVENT_MASK_BUTTON_UP:放開滑鼠按鍵。
  • GR_EVENT_MASK_BUTTON_DOWN:按下滑鼠按鍵。

因此選擇事件的程式碼應修改成:

   GrSelectEvents(wid, GR_EVENT_MASK_MOUSE_POSITION | 
                           GR_EVENT_MASK_BUTTON_UP |
                           GR_EVENT_MASK_BUTTON_DOWN);

接下來,要特別注意的地方是「顯示圖片的時機」。我們在這裡設計成在視窗顯示後、進入 event loop 前處理。首先是顯示圖片的程式寫法:

   GrMapWindow(wid);
   GrDrawImageBits(wid, gc, 0, 0, &image_jollen);

接著,修改 event loop,並加上處理以上 3 個事件(粗體字部份)的程式碼:

void event_handler (GR_EVENT *event)
{
   switch (event->type)
   {
      case GR_EVENT_TYPE_CLOSE_REQ: 
            GrClose();
      case GR_EVENT_TYPE_MOUSE_POSITION:
            position_event(&event->mouse);
            break;
      case GR_EVENT_TYPE_BUTTON_UP:
      case GR_EVENT_TYPE_BUTTON_DOWN:
            button_event(&event->button);
            break;
      default: break;
   }
}

對於滑鼠按鍵的處理方式為:當滑鼠被按下時,便記錄滑鼠的新座標,然後將視窗移到最上層;若此時使用者移動滑鼠,則將整個「視窗」移到新座標位置。如此一來,使用者就會看到 「整個圖片被托曳」的效果。

處理滑鼠按鍵的程式寫法如下:

void button_event(GR_EVENT_BUTTON *e)
{
   if (e->type == GR_EVENT_TYPE_BUTTON_DOWN) {
      newx = e->x;
      newy = e->y;

      button_down = 1;
      GrRaiseWindow(e->wid);
   } else {
      button_down = 0;
   }
}

先判斷目前所產生的事件是否為 GR_EVENT_TYPE_BUTTON_DOWN;如果是,才記錄新座標,並將 button_down 設為 1。若不是 GR_EVENT_TYPE_BUTTON_DOWN 事件,表示滑鼠按鍵已放開,此時將 button_down 設為 0。
處理滑鼠移動事件的程式寫法如下:

void position_event(GR_EVENT_MOUSE *e)
{
   if (!button_down) return;

   GrMoveWindow(e->wid, e->rootx-newx, e->rooty-newy);
}

先判斷 button_down 是否為 false,若 button_down等於0,表示滑鼠按鍵是放開的,因此不做任何動作。反正,若 button_down 為true,代表滑鼠按鍵仍「持續」按住,此時才能呼叫 GrMoveWindow() 移動視窗。

以下是 wm.c 的完整程式,粗體字是新加入或修改過的程式碼。

/*
 * Copyright(c) 2003,2004 www.jollen.org
 *
 * - Nano-X API example.
 * - wm.c
 *
 */

#include <stdio.h>
#define MWINCLUDECOLORS
#include <microwin/nano-X.h>
#include <microwin/nxcolors.h>

GR_WINDOW_ID wid;
GR_GC_ID gc;

/* 外部影像 */
extern GR_IMAGE_HDR image_jollen;

void event_handler (GR_EVENT *event);

/* 滑鼠座標 */
int newx, newy;
int button_down;

int main (void)
{
   if (GrOpen() < 0) {
        fprintf (stderr, "GrOpen failed");
        return -1;
   }

   gc = GrNewGC();
   GrSetGCForeground (gc, 0xFF0000);

   wid = GrNewWindowEx(GR_WM_PROPS_APPWINDOW |
                       GR_WM_PROPS_NODECORATE | 
                       GR_WM_PROPS_NOAUTOMOVE,
                       NULL,
                       GR_ROOT_WINDOW_ID, 
                       0, 0, 
                       image_jollen.width, /* 影像寬度 */
                       image_jollen.height /* 影像高度 */, 
                       0xFFFFFF);

   GrSelectEvents(wid, GR_EVENT_MASK_MOUSE_POSITION |
                    GR_EVENT_MASK_BUTTON_UP |
                    GR_EVENT_MASK_BUTTON_DOWN);

   GrMapWindow(wid);
   GrDrawImageBits(wid, gc, 0, 0, &image_jollen);

   GrMainLoop(event_handler);

   return 0;
}

void button_event(GR_EVENT_BUTTON *e)
{
   if (e->type == GR_EVENT_TYPE_BUTTON_DOWN) {
      newx = e->x;
      newy = e->y;

      button_down = 1;
      GrRaiseWindow(e->wid);
   } else {
      button_down = 0;
   }
}

void position_event(GR_EVENT_MOUSE *e)
{
   if (!button_down) return;

   GrMoveWindow(e->wid, e->rootx-newx, e->rooty-newy);
}

void event_handler (GR_EVENT *event)
{
   switch (event->type)
   {
      case GR_EVENT_TYPE_CLOSE_REQ: 
	   GrClose();
      case GR_EVENT_TYPE_MOUSE_POSITION:
	   position_event(&event->mouse);
	   break;
      case GR_EVENT_TYPE_BUTTON_UP:
      case GR_EVENT_TYPE_BUTTON_DOWN:
           button_event(&event->button);
	   break;
      default: break;
   }
}

注釋

  • 2007.01.13 編修(Revision)
Also See

Jollen's Blog 使用 Github issues 與讀者交流討論。請點擊上方的文章專屬 issue,或 open a new issue

您可透過電子郵件 jollen@jollen.org,或是 Linkedin 與我連絡。更歡迎使用微信,請搜尋 WeChat ID:jollentw