Browse Source

Merge pull request #7628 from RayKoopa/gui_resizable_popups

Added functionality for resizable WindowDialogs
Rémi Verschelde 8 years ago
parent
commit
f592d1ebfc
3 changed files with 187 additions and 71 deletions
  1. 157 52
      scene/gui/dialogs.cpp
  2. 17 1
      scene/gui/dialogs.h
  3. 13 18
      scene/resources/default_theme/default_theme.cpp

+ 157 - 52
scene/gui/dialogs.cpp

@@ -33,76 +33,145 @@
 
 void WindowDialog::_post_popup() {
 
-	dragging=false; //just in case
+	drag_type = DRAG_NONE; // just in case
 }
 
 bool WindowDialog::has_point(const Point2& p_point) const {
 
+	Rect2 r(Point2(), get_size());
 
-	int extra = get_constant("titlebar_height","WindowDialog");
-	Rect2 r( Point2(), get_size() );
-	r.pos.y-=extra;
-	r.size.y+=extra;
-	return r.has_point(p_point);
+	// Enlarge upwards for title bar.
+	int titlebar_height = get_constant("titlebar_height", "WindowDialog");
+	r.pos.y -= titlebar_height;
+	r.size.y += titlebar_height;
+
+	// Inflate by the resizable border thickness.
+	if (resizable) {
+		int scaleborder_size = get_constant("scaleborder_size", "WindowDialog");
+		r.pos.x -= scaleborder_size;
+		r.size.width += scaleborder_size * 2;
+		r.pos.y -= scaleborder_size;
+		r.size.height += scaleborder_size * 2;
+	}
 
+	return r.has_point(p_point);
 }
 
 void WindowDialog::_gui_input(const InputEvent& p_event) {
 
-	if (p_event.type == InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==BUTTON_LEFT) {
-
-		if (p_event.mouse_button.pressed && p_event.mouse_button.y < 0)
-			dragging=true;
-		else if (dragging && !p_event.mouse_button.pressed)
-			dragging=false;
+	if (p_event.type == InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index == BUTTON_LEFT) {
+
+		if (p_event.mouse_button.pressed) {
+			// Begin a possible dragging operation.
+			drag_type = _drag_hit_test(Point2(p_event.mouse_button.x, p_event.mouse_button.y));
+			if (drag_type != DRAG_NONE)
+				drag_offset = get_global_mouse_pos() - get_pos();
+				drag_offset_far = get_pos() + get_size() - get_global_mouse_pos();
+		} else if (drag_type != DRAG_NONE && !p_event.mouse_button.pressed) {
+			// End a dragging operation.
+			drag_type = DRAG_NONE;
+		}
 	}
 
-
-	if (p_event.type == InputEvent::MOUSE_MOTION && dragging) {
-
-		Point2 rel( p_event.mouse_motion.relative_x, p_event.mouse_motion.relative_y );
-		Point2 pos = get_pos();
-
-		pos+=rel;
-
-		if (pos.y<0)
-			pos.y=0;
-
-		set_pos(pos);
+	if (p_event.type == InputEvent::MOUSE_MOTION) {
+
+		if (drag_type == DRAG_NONE) {
+			// Update the cursor while moving along the borders.
+			CursorShape cursor = CURSOR_ARROW;
+			if (resizable) {
+				int preview_drag_type = _drag_hit_test(Point2(p_event.mouse_button.x, p_event.mouse_button.y));
+				switch (preview_drag_type) {
+					case DRAG_RESIZE_TOP:
+					case DRAG_RESIZE_BOTTOM:
+						cursor = CURSOR_VSIZE;
+						break;
+					case DRAG_RESIZE_LEFT:
+					case DRAG_RESIZE_RIGHT:
+						cursor = CURSOR_HSIZE;
+						break;
+					case DRAG_RESIZE_TOP + DRAG_RESIZE_LEFT:
+					case DRAG_RESIZE_BOTTOM + DRAG_RESIZE_RIGHT:
+						cursor = CURSOR_FDIAGSIZE;
+						break;
+					case DRAG_RESIZE_TOP + DRAG_RESIZE_RIGHT:
+					case DRAG_RESIZE_BOTTOM + DRAG_RESIZE_LEFT:
+						cursor = CURSOR_BDIAGSIZE;
+						break;
+				}
+			}
+			if (get_cursor_shape() != cursor)
+				set_default_cursor_shape(cursor);
+		} else {
+			// Update while in a dragging operation.
+			Point2 global_pos = get_global_mouse_pos();
+			global_pos.y = MAX(global_pos.y, 0); // Ensure title bar stays visible.
+
+			Rect2 rect = get_rect();
+			Size2 min_size = get_minimum_size();
+
+			if (drag_type == DRAG_MOVE) {
+				rect.pos = global_pos - drag_offset;
+			} else {
+				if (drag_type & DRAG_RESIZE_TOP) {
+					int bottom = rect.pos.y + rect.size.height;
+					int max_y = bottom - min_size.height;
+					rect.pos.y = MIN(global_pos.y - drag_offset.y, max_y);
+					rect.size.height = bottom - rect.pos.y;
+				} else if (drag_type & DRAG_RESIZE_BOTTOM) {
+					rect.size.height = global_pos.y - rect.pos.y + drag_offset_far.y;
+				}
+				if (drag_type & DRAG_RESIZE_LEFT) {
+					int right = rect.pos.x + rect.size.width;
+					int max_x = right - min_size.width;
+					rect.pos.x = MIN(global_pos.x - drag_offset.x, max_x);
+					rect.size.width = right - rect.pos.x;
+				} else if (drag_type & DRAG_RESIZE_RIGHT) {
+					rect.size.width = global_pos.x - rect.pos.x + drag_offset_far.x;
+				}
+			}
+
+			set_size(rect.size);
+			set_pos(rect.pos);
+		}
 	}
 }
 
 void WindowDialog::_notification(int p_what) {
 
-	switch(p_what) {
-
+	switch (p_what) {
 		case NOTIFICATION_DRAW: {
 
-			RID ci = get_canvas_item();
-			Size2 s = get_size();
-			Ref<StyleBox> st = get_stylebox("panel","WindowDialog");
-			st->draw(ci,Rect2(Point2(),s));
-			int th = get_constant("title_height","WindowDialog");
-			Color tc = get_color("title_color","WindowDialog");
-			Ref<Font> font = get_font("title_font","WindowDialog");
-			int ofs = (s.width-font->get_string_size(title).width)/2;
-			//int ofs = st->get_margin(MARGIN_LEFT);
-			draw_string(font,Point2(ofs,-th+font->get_ascent()),title,tc,s.width - st->get_minimum_size().width);
+			RID canvas = get_canvas_item();
+			Size2 size = get_size();
+
+			Ref<StyleBox> panel = get_stylebox("panel", "WindowDialog");
+			panel->draw(canvas, Rect2(Point2(), size));
 
+			int title_height = get_constant("title_height", "WindowDialog");
+			Color title_color = get_color("title_color", "WindowDialog");
+			Ref<Font> font = get_font("title_font", "WindowDialog");
+			int ofs = (size.width - font->get_string_size(title).width) / 2;
+			draw_string(font, Point2(ofs, -title_height + font->get_ascent()), title, title_color, size.width - panel->get_minimum_size().width);
 
 		} break;
+
 		case NOTIFICATION_THEME_CHANGED:
 		case NOTIFICATION_ENTER_TREE: {
+			close_button->set_normal_texture(get_icon("close", "WindowDialog"));
+			close_button->set_pressed_texture(get_icon("close", "WindowDialog"));
+			close_button->set_hover_texture(get_icon("close_hilite", "WindowDialog"));
+			close_button->set_anchor(MARGIN_LEFT, ANCHOR_END);
+			close_button->set_begin(Point2(get_constant("close_h_ofs", "WindowDialog"), -get_constant("close_v_ofs", "WindowDialog")));
+		} break;
 
-			close_button->set_normal_texture( get_icon("close","WindowDialog"));
-			close_button->set_pressed_texture( get_icon("close","WindowDialog"));
-			close_button->set_hover_texture( get_icon("close_hilite","WindowDialog"));
-			close_button->set_anchor(MARGIN_LEFT,ANCHOR_END);
-			close_button->set_begin( Point2( get_constant("close_h_ofs","WindowDialog"), -get_constant("close_v_ofs","WindowDialog") ));
-
+		case NOTIFICATION_MOUSE_EXIT: {
+			// Reset the mouse cursor when leaving the resizable window border.
+			if (resizable && !drag_type) {
+				if (get_default_cursor_shape() != CURSOR_ARROW)
+					set_default_cursor_shape(CURSOR_ARROW);
+			}
 		} break;
 	}
-
 }
 
 void WindowDialog::_closed() {
@@ -111,11 +180,48 @@ void WindowDialog::_closed() {
 	hide();
 }
 
+int WindowDialog::_drag_hit_test(const Point2& pos) const {
+	int drag_type = DRAG_NONE;
+
+	if (resizable) {
+		int titlebar_height = get_constant("titlebar_height", "WindowDialog");
+		int scaleborder_size = get_constant("scaleborder_size", "WindowDialog");
+		
+		Rect2 rect = get_rect();
+
+		if (pos.y < (-titlebar_height + scaleborder_size))
+			drag_type = DRAG_RESIZE_TOP;
+		else if (pos.y >= (rect.size.height - scaleborder_size))
+			drag_type = DRAG_RESIZE_BOTTOM;
+		if (pos.x < scaleborder_size)
+			drag_type |= DRAG_RESIZE_LEFT;
+		else if (pos.x >= (rect.size.width - scaleborder_size))
+			drag_type |= DRAG_RESIZE_RIGHT;
+	}
+
+	if (drag_type == DRAG_NONE && pos.y < 0)
+		drag_type = DRAG_MOVE;
+
+	return drag_type;
+}
+
 void WindowDialog::set_title(const String& p_title) {
 
 	title=XL_MESSAGE(p_title);
 	update();
 }
+String WindowDialog::get_title() const {
+
+	return title;
+}
+
+void WindowDialog::set_resizable(bool p_resizable) {
+	resizable = p_resizable;
+}
+bool WindowDialog::get_resizable() const {
+	return resizable;
+}
+
 
 Size2 WindowDialog::get_minimum_size() const {
 
@@ -127,11 +233,6 @@ Size2 WindowDialog::get_minimum_size() const {
 }
 
 
-String WindowDialog::get_title() const {
-
-	return title;
-}
-
 
 TextureButton *WindowDialog::get_close_button() {
 
@@ -144,19 +245,23 @@ void WindowDialog::_bind_methods() {
 	ClassDB::bind_method( D_METHOD("_gui_input"),&WindowDialog::_gui_input);
 	ClassDB::bind_method( D_METHOD("set_title","title"),&WindowDialog::set_title);
 	ClassDB::bind_method( D_METHOD("get_title"),&WindowDialog::get_title);
+	ClassDB::bind_method( D_METHOD("set_resizable","resizable"),&WindowDialog::set_resizable);
+	ClassDB::bind_method( D_METHOD("get_resizable"), &WindowDialog::get_resizable);
 	ClassDB::bind_method( D_METHOD("_closed"),&WindowDialog::_closed);
 	ClassDB::bind_method( D_METHOD("get_close_button:TextureButton"),&WindowDialog::get_close_button);
 
 	ADD_PROPERTY( PropertyInfo(Variant::STRING,"window_title",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_DEFAULT_INTL),"set_title","get_title");
+	ADD_PROPERTY( PropertyInfo(Variant::BOOL,"resizable",PROPERTY_HINT_NONE,"",PROPERTY_USAGE_DEFAULT_INTL),"set_resizable","get_resizable");
 }
 
 WindowDialog::WindowDialog() {
 
 	//title="Hello!";
-	dragging=false;
-	close_button = memnew( TextureButton );
+	drag_type = DRAG_NONE;
+	resizable = false;
+	close_button = memnew(TextureButton);
 	add_child(close_button);
-	close_button->connect("pressed",this,"_closed");
+	close_button->connect("pressed", this, "_closed");
 
 }
 
@@ -186,7 +291,7 @@ PopupDialog::~PopupDialog() {
 }
 
 
-//
+// AcceptDialog
 
 
 void AcceptDialog::_post_popup() {

+ 17 - 1
scene/gui/dialogs.h

@@ -44,12 +44,26 @@ class WindowDialog : public Popup {
 
 	GDCLASS(WindowDialog,Popup);
 
+	enum DRAG_TYPE {
+		DRAG_NONE = 0,
+		DRAG_MOVE = 1,
+		DRAG_RESIZE_TOP = 1 << 1,
+		DRAG_RESIZE_RIGHT = 1 << 2,
+		DRAG_RESIZE_BOTTOM = 1 << 3,
+		DRAG_RESIZE_LEFT = 1 << 4
+	};
+
 	TextureButton *close_button;
 	String title;
-	bool dragging;
+	int drag_type;
+	Point2 drag_offset;
+	Point2 drag_offset_far;
+	bool resizable;
 
 	void _gui_input(const InputEvent& p_event);
 	void _closed();
+	int _drag_hit_test(const Point2& pos) const;
+
 protected:
 	virtual void _post_popup();
 
@@ -63,6 +77,8 @@ public:
 
 	void set_title(const String& p_title);
 	String get_title() const;
+	void set_resizable(bool p_resizable);
+	bool get_resizable() const;
 
 	Size2 get_minimum_size() const;
 

+ 13 - 18
scene/resources/default_theme/default_theme.cpp

@@ -583,24 +583,19 @@ void fill_default_theme(Ref<Theme>& t, const Ref<Font> & default_font, const Ref
 
 	// WindowDialog
 
-	Ref<StyleBoxTexture> style_pp_win = sb_expand(make_stylebox( popup_window_png,10,30,10,8),8,26,8,4);
-	/*for(int i=0;i<4;i++)
-		style_pp_win->set_expand_margin_size((Margin)i,3);
-	style_pp_win->set_expand_margin_size(MARGIN_TOP,26);*/
-
-	t->set_stylebox("panel","WindowDialog", style_pp_win );
-
-	t->set_icon("close","WindowDialog", make_icon( close_png ) );
-	t->set_icon("close_hilite","WindowDialog", make_icon( close_hl_png ) );
-
-	t->set_font("title_font","WindowDialog", large_font );
-
-	t->set_color("title_color","WindowDialog", Color(0,0,0) );
-
-	t->set_constant("close_h_ofs","WindowDialog", 22 *scale);
-	t->set_constant("close_v_ofs","WindowDialog", 20 *scale);
-	t->set_constant("titlebar_height","WindowDialog", 18 *scale);
-	t->set_constant("title_height","WindowDialog", 20 *scale);
+	Ref<StyleBoxTexture> style_pp_win = sb_expand(make_stylebox(popup_window_png, 10, 26, 10, 8), 8, 24, 8, 6);
+	t->set_stylebox("panel", "WindowDialog", style_pp_win);
+	t->set_constant("titlebar_height", "WindowDialog", 20 * scale);
+	t->set_constant("scaleborder_size", "WindowDialog", 4);
+
+	t->set_font("title_font", "WindowDialog", large_font);
+	t->set_color("title_color", "WindowDialog", Color(0, 0, 0));
+	t->set_constant("title_height", "WindowDialog", 18 * scale);
+
+	t->set_icon("close", "WindowDialog", make_icon(close_png));
+	t->set_icon("close_hilite", "WindowDialog", make_icon(close_hl_png));
+	t->set_constant("close_h_ofs", "WindowDialog", 18 * scale);
+	t->set_constant("close_v_ofs", "WindowDialog", 18 * scale);
 
 
 	// File Dialog