hi-7
リニアメニューとパイメニューを作ってみましたよ。*1
作ってみて思ったのだけど、パイメニューは色々と面倒ですね。リニアメニューだと、項目数とか項目名の長さとかは、あまり気にしなくてもいいけど、パイメニューだと、項目数が多いと細かく分かれて選びにくいし、項目名が長いと入りきらなかったり、無理して入れようとするとメニューがすごく大きくなってしまうし。
決まり切った項目ならいいのかな。
↓オブジェクト指向って何だっけ。
#include <iostream> using namespace std; #include <X11/Xlib.h> #include <X11/X.h> #include <X11/Xutil.h> #include <X11/keysym.h> #include <locale.h> #define die { fprintf( stderr, "die at line %d.\n", __LINE__ ); exit(EXIT_FAILURE); } #define PI 3.1415 #define WIDTH 200 #define HEIGHT 200 #define FONTNAME "*-fixed-medium-r-normal--24-*" #define LINE_WIDTH 3 #define R 100 //pの半径。 int menu_type = 0; #define TYPE_L 1 #define TYPE_P 2 /* 1:l, 2:p */ /* 三つ子の魂百まで */ /* というか、一度設定したら変更しない。 */ unsigned long get_color(Display *d, Colormap cmap, char *color_name) { XColor exact_color, screen_color; XAllocNamedColor(d, cmap, color_name, &screen_color, &exact_color); return screen_color.pixel; } struct triangle{ int x1, y1; int x2, y2; int x3, y3; }; int position( int x1, int y1, int x2, int y2, int x, int y ){ /* (x1,y1)と(x2,y2)を通る直線の、上か下か、または(x1==x2)右か左か。 */ if( x1 == x2 ) return ( x1 < x )? 2 : 3; double tan = ( y2 - y1 ) / (double)( x2 - x1 ); return ( y - y1 < tan * ( x - x1 ) )? 0 : 1; } bool on_triangle( struct triangle tri, int x, int y ){ if( ( position( tri.x1, tri.y1, tri.x2, tri.y2, tri.x3, tri.y3 ) == position( tri.x1, tri.y1, tri.x2, tri.y2, x, y ) ) && ( position( tri.x2, tri.y2, tri.x3, tri.y3, tri.x1, tri.y1 ) == position( tri.x2, tri.y2, tri.x3, tri.y3, x, y ) ) && ( position( tri.x3, tri.y3, tri.x1, tri.y1, tri.x2, tri.y2 ) == position( tri.x3, tri.y3, tri.x1, tri.y1, x, y ) ) ){ return true; }else{ return false; } } struct iroiro{ Display *d; Window window; GC gc; XOC xoc; XRectangle logical; GC over_back_gc, over_fore_gc; }; struct label{ wchar_t * text; int length; int x, y; //メニューの中心からの相対位置。 int width, height; //p }; struct menudata{ struct label * labels; struct triangle * triangles; //p メニューの中心からの相対位置。 int n; int x, y; // メニューの中心。 int width, height; //l メニュー全体の }; struct menudata make_menudata( struct iroiro iroiro, struct label * labels ){ struct menudata menudata; XRectangle logical; int i, n; int height; menudata.labels = labels; if( menu_type == TYPE_L ){ menudata.width = 0; menudata.height = 0; }//TYPE_L for( i=0; labels[i].length!=0; i++ ){ XwcTextExtents( iroiro.xoc, labels[i].text, labels[i].length, NULL, &logical ); menudata.labels[i].x = - logical.x; // 仮。 menudata.labels[i].y = - logical.y; // 仮。 if( menu_type == TYPE_L ){ if( menudata.width < logical.width ) menudata.width = logical.width; if( menudata.height < logical.height ) menudata.height = logical.height; }else if( menu_type == TYPE_P ){ menudata.labels[i].width = logical.width; menudata.labels[i].height = logical.height; }//TYPE } n = i; menudata.n = n; if( menu_type == TYPE_L ){ for( i=0; i<n; i++ ){ menudata.labels[i].x += LINE_WIDTH; menudata.labels[i].y += LINE_WIDTH + menudata.height * i; } }//TYPE_L if( menu_type == TYPE_P ){ menudata.triangles = new struct triangle[n]; double prad = 2 * PI / n; // 1個の角度。 menudata.triangles[n-1].x1 = menudata.triangles[n-1].y1 = 0; menudata.triangles[n-1].x3 = menudata.triangles[0].x2 = (int)( R * sin( - prad / 2 ) ); menudata.triangles[n-1].y3 = menudata.triangles[0].y2 = (int)( - R * cos( - prad / 2 ) ); for( i=0; i<n-1; i++ ){ menudata.triangles[i].x1 = menudata.triangles[i].y1 = 0; menudata.triangles[i].x3 = menudata.triangles[i+1].x2 = (int)( R * sin( prad * ( i + 0.5 ) ) ); menudata.triangles[i].y3 = menudata.triangles[i+1].y2 = (int)( - R * cos( prad * ( i + 0.5 ) ) ); } for( i=0; i<n; i++ ){ menudata.labels[i].x += ( 0 + menudata.triangles[i].x2 + menudata.triangles[i].x3 ) / 3 - menudata.labels[i].width / 2; menudata.labels[i].y += ( 0 + menudata.triangles[i].y2 + menudata.triangles[i].y3 ) / 3 - menudata.labels[i].height / 2; } }//TYPE_P return menudata; } void menu( struct iroiro iroiro, struct menudata menudata ){ struct label * labels = menudata.labels; int x = menudata.x; int y = menudata.y; int n = menudata.n; XClearWindow( iroiro.d, iroiro.window ); if( menu_type == TYPE_L ){ struct label* labels = menudata.labels; XDrawRectangle( iroiro.d, iroiro.window, iroiro.gc, x, y, menudata.width + LINE_WIDTH * 2, menudata.height * n + LINE_WIDTH * 2 ); }else if( menu_type == TYPE_P ){ XPoint p[3]; for( int i=0; i<n; i++ ){ p[0].x = x + menudata.triangles[i].x1; p[0].y = y + menudata.triangles[i].y1; p[1].x = x + menudata.triangles[i].x2; p[1].y = y + menudata.triangles[i].y2; p[2].x = x + menudata.triangles[i].x3; p[2].y = y + menudata.triangles[i].y3; XDrawLines( iroiro.d, iroiro.window, iroiro.gc, p, 3, CoordModeOrigin ); } } for( int i=0; i<n; i++ ){ XwcDrawString( iroiro.d, iroiro.window, iroiro.xoc, iroiro.gc, x + labels[i].x, y + labels[i].y, labels[i].text, labels[i].length ); } } int get_index( struct menudata menudata, int x, int y ){ if( menu_type == TYPE_L ){ if( ( x < menudata.x + LINE_WIDTH ) || ( menudata.x + LINE_WIDTH + menudata.width < x ) || ( y < menudata.y + LINE_WIDTH ) || ( menudata.y + LINE_WIDTH + menudata.height * menudata.n < y ) ){ return -1; } for( int i=0; i<menudata.n; i++ ){ if( y < menudata.y + LINE_WIDTH + menudata.height * (i+1) ){ return i; } } }else if( menu_type == TYPE_P ){ if( ( menudata.x - LINE_WIDTH < x ) && ( x < menudata.x + LINE_WIDTH ) && ( menudata.y - LINE_WIDTH < y ) && ( y < menudata.y + LINE_WIDTH ) ){ return -1; } for( int i=0; i<menudata.n; i++ ){ if( on_triangle( menudata.triangles[i], x - menudata.x, y - menudata.y ) ) return i; } }//TYPE return -1; } void over_menu( struct iroiro iroiro, struct menudata menudata, int index ){ menu( iroiro, menudata ); if( index < 0 ) return; int x = menudata.x; int y = menudata.y; struct label * labels = menudata.labels; XRectangle logical = iroiro.logical; int i = index; if( menu_type == TYPE_L ){ XFillRectangle( iroiro.d, iroiro.window, iroiro.over_back_gc, x + LINE_WIDTH, y + menudata.height * i + LINE_WIDTH, menudata.width, menudata.height ); }else if( menu_type == TYPE_P ){ XPoint p[3]; p[0].x = x + menudata.triangles[i].x1; p[0].y = y + menudata.triangles[i].y1; p[1].x = x + menudata.triangles[i].x2; p[1].y = y + menudata.triangles[i].y2; p[2].x = x + menudata.triangles[i].x3; p[2].y = y + menudata.triangles[i].y3; XFillPolygon( iroiro.d, iroiro.window, iroiro.over_back_gc, p, 3, Nonconvex, CoordModeOrigin ); }//TYPE XwcDrawString( iroiro.d, iroiro.window, iroiro.xoc, iroiro.over_fore_gc, x + menudata.labels[i].x, y + menudata.labels[i].y, labels[i].text, labels[i].length ); } int main(int argc, char ** argv){ Display *d; Colormap cmap; int screen; Window root, window; // --- 国際化テキストを扱うための準備。 if (setlocale(LC_ALL, "")==NULL) { cerr << "Can't set locale.\n"; exit(1); } if ((d=XOpenDisplay(NULL))==NULL) { cerr << "Can't open display.\n"; exit(1); } if (XSupportsLocale()==False) { cerr << "unsupported locale.\n"; exit(1); } if (XSetLocaleModifiers("")==NULL) { cerr << "Can't set locale modifiers.\n"; exit(1); } // --- Xクライアントの一般的な初期化。 root = DefaultRootWindow(d); screen = DefaultScreen(d); cmap = DefaultColormap(d, screen); window = XCreateSimpleWindow(d, root, 0, 0, WIDTH, HEIGHT, 3, BlackPixel(d, screen), WhitePixel(d, screen)); // --- テキスト出力関連の初期化。 XOM xom = XOpenOM(d, NULL, NULL, NULL); XOC xoc = XCreateOC(xom, XNBaseFontName, FONTNAME, NULL); if(xoc == NULL){ cerr << "Can't create OC.\n"; exit(1); } XRectangle logical; XwcTextExtents(xoc, L"iあ", 2, NULL, &logical); XGCValues xgcv; xgcv.line_width = LINE_WIDTH; GC gc=XCreateGC(d, window, GCLineWidth, &xgcv); xgcv.foreground = get_color( d, cmap, "blue" ); GC over_back_gc = XCreateGC( d, window, GCForeground, &xgcv ); xgcv.foreground = get_color( d, cmap, "white" ); GC over_fore_gc = XCreateGC( d, window, GCForeground, &xgcv ); XSelectInput(d, window, ExposureMask|ButtonPressMask|ButtonReleaseMask|KeyPressMask|PointerMotionMask); XMapWindow(d, window); struct iroiro iroiro; iroiro.d = d; iroiro.window = window; iroiro.gc = gc; iroiro.xoc = xoc; iroiro.logical = logical; iroiro.over_back_gc = over_back_gc; iroiro.over_fore_gc = over_fore_gc; if( argc < 2 ) die; if( argv[1][1] == 'L' ){ menu_type = TYPE_L; }else if( argv[1][1] == 'P' ){ menu_type = TYPE_P; }else{ die; } struct label labels[argc-1]; for( int i=2; i<argc; i++ ){ wchar_t * buf; int len = mbstowcs( NULL, argv[i], 0 ) + 1; buf = new wchar_t[len]; labels[i-2].length = mbstowcs( buf, argv[i], len ); labels[i-2].text = buf; } labels[argc-2].length = 0; struct menudata menudata = make_menudata( iroiro, labels ); int overindex = -1; bool open_menu = false; XEvent e; int index; for( bool done=false; done == false; ){ XNextEvent(d, &e); switch( e.type ){ case KeyPress : // 終了する。 done = true; break; case ButtonPress : if( !open_menu ){ open_menu = true; menudata.x = e.xbutton.x; menudata.y = e.xbutton.y; menu( iroiro, menudata ); }else{ // メニューが開いてる時。 index = get_index( menudata, e.xbutton.x, e.xbutton.y ); if( index >= 0 ) break; // メニュー外の時。 open_menu = false; XClearWindow( d, window ); } break; case ButtonRelease : if( !open_menu ) break; // メニューが開いてる時のみ。 index = get_index( menudata, e.xbutton.x, e.xbutton.y ); if( index >= 0 ){ printf( "[%d] = '%s'\n", index, argv[index+2] ); open_menu = false; XClearWindow( d, window ); } break; case MotionNotify : if( !open_menu ) break; // メニューが開いてる時のみ。 index = get_index( menudata, e.xmotion.x, e.xmotion.y ); if( overindex == index ) break; overindex = index; over_menu( iroiro, menudata, index ); break; } } return 0; }
*1:〆切過ぎてますね。