1 /*$
2  Copyright (C) 2013-2020 Azel.
3 
4  This file is part of AzPainter.
5 
6  AzPainter is free software: you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation, either version 3 of the License, or
9  (at your option) any later version.
10 
11  AzPainter is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15 
16  You should have received a copy of the GNU General Public License
17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 $*/
19 
20 /*****************************************
21  * mButton  [ボタン]
22  *****************************************/
23 
24 #include <string.h>
25 
26 #include "mDef.h"
27 
28 #include "mButton.h"
29 #include "mWidget.h"
30 #include "mFont.h"
31 #include "mPixbuf.h"
32 #include "mEvent.h"
33 #include "mSysCol.h"
34 #include "mKeyDef.h"
35 
36 
37 //-----------------------
38 
39 #define BTT_DEFW  64
40 #define BTT_DEFH  21
41 
42 enum MBUTTON_FLAGS
43 {
44 	MBUTTON_FLAGS_PRESSED = 1<<0,
45 	MBUTTON_FLAGS_GRAB    = 1<<1
46 };
47 
48 //-----------------------
49 
50 
51 /******************//**
52 
53 @defgroup button mButton
54 @brief ボタン
55 
56 <h3>継承</h3>
57 mWidget \> mButton
58 
59 @ingroup group_widget
60 
61 @{
62 
63 @file mButton.h
64 @struct _mButton
65 @struct mButtonData
66 @enum MBUTTON_STYLE
67 @enum MBUTTON_NOTIFY
68 
69 @var MBUTTON_NOTIFY::MBUTTON_N_PRESS
70 ボタンが押された
71 
72 ********************/
73 
74 
75 //==========================
76 
77 
78 /** グラブ状態解除 */
79 
_grab_release(mButton * p,mBool pressed)80 static void _grab_release(mButton *p,mBool pressed)
81 {
82 	//押し状態解除
83 
84 	if(p->btt.flags & MBUTTON_FLAGS_GRAB)
85 	{
86 		mWidgetUngrabPointer(M_WIDGET(p));
87 
88 		p->btt.flags &= ~MBUTTON_FLAGS_GRAB;
89 		p->btt.press_key = 0;
90 
91 		mButtonSetPress(p, FALSE);
92 	}
93 
94 	//ハンドラ実行
95 
96 	if(pressed)
97 		(p->btt.onPressed)(p);
98 }
99 
100 /** ボタン/キー押し時 */
101 
_pressed(mButton * p)102 static void _pressed(mButton  *p)
103 {
104 	p->btt.flags |= MBUTTON_FLAGS_GRAB;
105 
106 	mButtonSetPress(p, TRUE);
107 }
108 
109 /** onPress() デフォルト */
110 
_handle_pressed(mButton * p)111 static void _handle_pressed(mButton  *p)
112 {
113 	mWidgetAppendEvent_notify(NULL, M_WIDGET(p), MBUTTON_N_PRESS, 0, 0);
114 }
115 
116 
117 //==========================
118 
119 
120 /** 解放処理 */
121 
mButtonDestroyHandle(mWidget * p)122 void mButtonDestroyHandle(mWidget *p)
123 {
124 	mButton *btt = M_BUTTON(p);
125 
126 	M_FREE_NULL(btt->btt.text);
127 }
128 
129 /** ボタン作成 */
130 
mButtonCreate(mWidget * parent,int id,uint32_t style,uint32_t fLayout,uint32_t marginB4,const char * text)131 mButton *mButtonCreate(mWidget *parent,int id,uint32_t style,
132 	uint32_t fLayout,uint32_t marginB4,const char *text)
133 {
134 	mButton *p;
135 
136 	p = mButtonNew(0, parent, style);
137 	if(!p) return NULL;
138 
139 	p->wg.id = id;
140 	p->wg.fLayout = fLayout;
141 
142 	mWidgetSetMargin_b4(M_WIDGET(p), marginB4);
143 
144 	mButtonSetText(p, text);
145 
146 	return p;
147 }
148 
149 /** ボタン作成 */
150 
mButtonNew(int size,mWidget * parent,uint32_t style)151 mButton *mButtonNew(int size,mWidget *parent,uint32_t style)
152 {
153 	mButton *p;
154 
155 	if(size < sizeof(mButton)) size = sizeof(mButton);
156 
157 	p = (mButton *)mWidgetNew(size, parent);
158 	if(!p) return NULL;
159 
160 	p->wg.destroy = mButtonDestroyHandle;
161 	p->wg.calcHint = mButtonCalcHintHandle;
162 	p->wg.draw = mButtonDrawHandle;
163 	p->wg.event = mButtonEventHandle;
164 
165 	p->wg.fState |= MWIDGET_STATE_TAKE_FOCUS;
166 	p->wg.fEventFilter |= MWIDGET_EVENTFILTER_POINTER | MWIDGET_EVENTFILTER_KEY;
167 	p->wg.fAcceptKeys = MWIDGET_ACCEPTKEY_ENTER;
168 
169 	p->btt.style = style;
170 	p->btt.onPressed = _handle_pressed;
171 
172 	return p;
173 }
174 
175 /** テキストセット */
176 
mButtonSetText(mButton * p,const char * text)177 void mButtonSetText(mButton *p,const char *text)
178 {
179 	mFree(p->btt.text);
180 
181 	p->btt.text = mStrdup(text);
182 
183 	mWidgetCalcHintSize(M_WIDGET(p));
184 	mWidgetUpdate(M_WIDGET(p));
185 }
186 
187 /** 押し状態変更 */
188 
mButtonSetPress(mButton * p,mBool press)189 void mButtonSetPress(mButton *p,mBool press)
190 {
191 	int now;
192 
193 	now = ((p->btt.flags & MBUTTON_FLAGS_PRESSED) != 0);
194 
195 	if(!now != !press)
196 	{
197 		if(press)
198 			p->btt.flags |= MBUTTON_FLAGS_PRESSED;
199 		else
200 			p->btt.flags &= ~MBUTTON_FLAGS_PRESSED;
201 
202 		mWidgetUpdate(M_WIDGET(p));
203 	}
204 }
205 
206 /** 押された状態か */
207 
mButtonIsPressed(mButton * p)208 mBool mButtonIsPressed(mButton *p)
209 {
210 	return ((p->btt.flags & MBUTTON_FLAGS_PRESSED) != 0);
211 }
212 
213 /** ボタンのベースを描画 */
214 
mButtonDrawBase(mButton * p,mPixbuf * pixbuf,mBool pressed)215 void mButtonDrawBase(mButton *p,mPixbuf *pixbuf,mBool pressed)
216 {
217 	uint32_t flags = 0;
218 
219 	if(pressed) flags |= MPIXBUF_DRAWBTT_PRESS;
220 	if(p->wg.fState & MWIDGET_STATE_FOCUSED) flags |= MPIXBUF_DRAWBTT_FOCUS;
221 	if(!(p->wg.fState & MWIDGET_STATE_ENABLED)) flags |= MPIXBUF_DRAWBTT_DISABLE;
222 	if(p->wg.fState & MWIDGET_STATE_ENTER_DEFAULT) flags |= MPIXBUF_DRAWBTT_DEFAULT_BUTTON;
223 
224 	mPixbufDrawButton(pixbuf, 0, 0, p->wg.w, p->wg.h, flags);
225 }
226 
227 
228 //========================
229 // ハンドラ
230 //========================
231 
232 
233 /** サイズ計算 */
234 
mButtonCalcHintHandle(mWidget * p)235 void mButtonCalcHintHandle(mWidget *p)
236 {
237 	mButton *btt = M_BUTTON(p);
238 	mFont *font = mWidgetGetFont(p);
239 	int w,h;
240 
241 	//幅
242 
243 	w = mFontGetTextWidth(font, btt->btt.text, -1);
244 
245 	btt->btt.textW = w;
246 
247 	if(btt->btt.style & MBUTTON_S_REAL_W)
248 		w += 8;
249 	else
250 	{
251 		w += 10;
252 
253 		if(w < BTT_DEFW)
254 		{
255 			w = BTT_DEFW;
256 			if((w - btt->btt.textW) & 1) w++;
257 		}
258 	}
259 
260 	//高さ
261 
262 	h = font->height;
263 
264 	if(btt->btt.style & MBUTTON_S_REAL_H)
265 		h += 6;
266 	else
267 	{
268 		h += 8;
269 
270 		if(h < BTT_DEFH)
271 		{
272 			h = BTT_DEFH;
273 			if((h - font->height) & 1) h++;
274 		}
275 	}
276 
277 	//REAL_W 時は高さが最小
278 
279 	if((btt->btt.style & MBUTTON_S_REAL_W) && w < h)
280 		w = h;
281 
282 	p->hintW = w;
283 	p->hintH = h;
284 }
285 
286 /** イベント */
287 
mButtonEventHandle(mWidget * wg,mEvent * ev)288 int mButtonEventHandle(mWidget *wg,mEvent *ev)
289 {
290 	mButton *btt = M_BUTTON(wg);
291 
292 	switch(ev->type)
293 	{
294 		case MEVENT_POINTER:
295 			if(ev->pt.type == MEVENT_POINTER_TYPE_PRESS
296 				|| ev->pt.type == MEVENT_POINTER_TYPE_DBLCLK)
297 			{
298 				//押し
299 
300 				if(ev->pt.btt == M_BTT_LEFT
301 					&& !(btt->btt.flags & MBUTTON_FLAGS_GRAB))
302 				{
303 					mWidgetSetFocus_update(wg, FALSE);
304 					mWidgetGrabPointer(wg);
305 
306 					_pressed(btt);
307 				}
308 			}
309 			else if(ev->pt.type == MEVENT_POINTER_TYPE_RELEASE)
310 			{
311 				//離し
312 
313 				if(ev->pt.btt == M_BTT_LEFT
314 					&& (btt->btt.flags & MBUTTON_FLAGS_GRAB)
315 					&& btt->btt.press_key == 0)
316 					_grab_release(btt, TRUE);
317 			}
318 			return TRUE;
319 
320 		//キー
321 		case MEVENT_KEYDOWN:
322 			if((ev->key.code == MKEY_SPACE || ev->key.code == MKEY_ENTER)
323 				&& !(btt->btt.flags & MBUTTON_FLAGS_GRAB))
324 			{
325 				btt->btt.press_key = ev->key.code;
326 
327 				_pressed(btt);
328 
329 				return TRUE;
330 			}
331 			break;
332 		case MEVENT_KEYUP:
333 			if((btt->btt.flags & MBUTTON_FLAGS_GRAB)
334 				&& btt->btt.press_key == ev->key.code)
335 			{
336 				_grab_release(btt, TRUE);
337 				return TRUE;
338 			}
339 			break;
340 
341 		case MEVENT_FOCUS:
342 			//フォーカスアウト時、グラブ状態解除
343 			if(ev->focus.bOut)
344 				_grab_release(btt, FALSE);
345 
346 			mWidgetUpdate(wg);
347 			return TRUE;
348 	}
349 
350 	return FALSE;
351 }
352 
353 /** 描画 */
354 
mButtonDrawHandle(mWidget * p,mPixbuf * pixbuf)355 void mButtonDrawHandle(mWidget *p,mPixbuf *pixbuf)
356 {
357 	mButton *btt = M_BUTTON(p);
358 	mFont *font;
359 	int tx,ty,press;
360 
361 	font = mWidgetGetFont(p);
362 
363 	press = ((btt->btt.flags & MBUTTON_FLAGS_PRESSED) != 0);
364 
365 	//テキスト位置
366 
367 	tx = (p->w - btt->btt.textW) >> 1;
368 	ty = (p->h - font->height) >> 1;
369 
370 	if(press) tx++, ty++;
371 
372 	//ボタン
373 
374 	mButtonDrawBase(btt, pixbuf, press);
375 
376 	//テキスト
377 
378 	mFontDrawText(font, pixbuf, tx, ty, btt->btt.text, -1,
379 		(p->fState & MWIDGET_STATE_ENABLED)? MSYSCOL_RGB(TEXT): MSYSCOL_RGB(TEXT_DISABLE));
380 }
381 
382 /** @} */
383