diff options
Diffstat (limited to 'scene/gui')
97 files changed, 9341 insertions, 3574 deletions
diff --git a/scene/gui/SCsub b/scene/gui/SCsub index 055d2f2474..bbe59b3054 100644 --- a/scene/gui/SCsub +++ b/scene/gui/SCsub @@ -3,5 +3,3 @@ Import('env') env.add_source_files(env.scene_sources,"*.cpp") Export('env') - - diff --git a/scene/gui/base_button.cpp b/scene/gui/base_button.cpp index 0167687621..0c63a3bc74 100644 --- a/scene/gui/base_button.cpp +++ b/scene/gui/base_button.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -148,6 +148,7 @@ void BaseButton::_input_event(InputEvent p_event) { update(); } } break; + case InputEvent::ACTION: case InputEvent::JOYSTICK_BUTTON: case InputEvent::KEY: { @@ -254,6 +255,16 @@ void BaseButton::_notification(int p_what) { group->_remove_button(this); } + if (p_what==NOTIFICATION_VISIBILITY_CHANGED && !is_visible()) { + + if (!toggle_mode) { + status.pressed = false; + } + status.hovering = false; + status.press_attempt = false; + status.pressing_inside = false; + status.pressing_button = 0; + } } void BaseButton::pressed() { @@ -279,12 +290,12 @@ void BaseButton::set_disabled(bool p_disabled) { set_focus_mode(FOCUS_NONE); else set_focus_mode(FOCUS_ALL); -}; +} bool BaseButton::is_disabled() const { return status.disabled; -}; +} void BaseButton::set_pressed(bool p_pressed) { @@ -389,9 +400,10 @@ void BaseButton::_bind_methods() { ADD_SIGNAL( MethodInfo("pressed" ) ); ADD_SIGNAL( MethodInfo("released" ) ); ADD_SIGNAL( MethodInfo("toggled", PropertyInfo( Variant::BOOL,"pressed") ) ); - ADD_PROPERTY( PropertyInfo( Variant::BOOL, "disabled"), _SCS("set_disabled"), _SCS("is_disabled")); + ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "disabled"), _SCS("set_disabled"), _SCS("is_disabled")); ADD_PROPERTY( PropertyInfo( Variant::BOOL, "toggle_mode"), _SCS("set_toggle_mode"), _SCS("is_toggle_mode")); - ADD_PROPERTY( PropertyInfo( Variant::BOOL, "click_on_press"), _SCS("set_click_on_press"), _SCS("get_click_on_press")); + ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "is_pressed"), _SCS("set_pressed"), _SCS("is_pressed")); + ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "click_on_press"), _SCS("set_click_on_press"), _SCS("get_click_on_press")); BIND_CONSTANT( DRAW_NORMAL ); diff --git a/scene/gui/base_button.h b/scene/gui/base_button.h index a376591ebb..e187a85eae 100644 --- a/scene/gui/base_button.h +++ b/scene/gui/base_button.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/box_container.cpp b/scene/gui/box_container.cpp index 216c6d7122..b63b3de530 100644 --- a/scene/gui/box_container.cpp +++ b/scene/gui/box_container.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -44,7 +44,7 @@ void BoxContainer::_resort() { Size2i new_size=get_size();; - int sep=get_constant("separation",vertical?"VBoxContainer":"HBoxContainer"); + int sep=get_constant("separation");//,vertical?"VBoxContainer":"HBoxContainer"); bool first=true; int children_count=0; @@ -99,8 +99,10 @@ void BoxContainer::_resort() { elements exist */ + bool has_stretched = false; while(stretch_ratio_total>0) { // first of all, dont even be here if no stretchable objects exist + has_stretched = true; bool refit_successful=true; //assume refit-test will go well for(int i=0;i<get_child_count();i++) { @@ -143,6 +145,18 @@ void BoxContainer::_resort() { int ofs=0; + if (!has_stretched) { + switch (align) { + case ALIGN_BEGIN: + break; + case ALIGN_CENTER: + ofs = stretch_diff / 2; + break; + case ALIGN_END: + ofs = stretch_diff; + break; + } + } first=true; int idx=0; @@ -202,7 +216,7 @@ Size2 BoxContainer::get_minimum_size() const { /* Calculate MINIMUM SIZE */ Size2i minimum; - int sep=get_constant("separation",vertical?"VBoxContainer":"HBoxContainer"); + int sep=get_constant("separation");//,vertical?"VBoxContainer":"HBoxContainer"); bool first=true; @@ -254,6 +268,15 @@ void BoxContainer::_notification(int p_what) { } } +void BoxContainer::set_alignment(AlignMode p_align) { + align = p_align; + _resort(); +} + +BoxContainer::AlignMode BoxContainer::get_alignment() const { + return align; +} + void BoxContainer::add_spacer(bool p_begin) { Control *c = memnew( Control ); @@ -270,10 +293,23 @@ void BoxContainer::add_spacer(bool p_begin) { BoxContainer::BoxContainer(bool p_vertical) { vertical=p_vertical; + align = ALIGN_BEGIN; // set_ignore_mouse(true); set_stop_mouse(false); } +void BoxContainer::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("get_alignment"),&BoxContainer::get_alignment); + ObjectTypeDB::bind_method(_MD("set_alignment","alignment"),&BoxContainer::set_alignment); + + BIND_CONSTANT( ALIGN_BEGIN ); + BIND_CONSTANT( ALIGN_CENTER ); + BIND_CONSTANT( ALIGN_END ); + + ADD_PROPERTY( PropertyInfo(Variant::INT,"alignment", PROPERTY_HINT_ENUM, "Begin,Center,End"), _SCS("set_alignment"),_SCS("get_alignment") ); + +} MarginContainer* VBoxContainer::add_margin_child(const String& p_label,Control *p_control,bool p_expand) { diff --git a/scene/gui/box_container.h b/scene/gui/box_container.h index fb305f0423..c357814baf 100644 --- a/scene/gui/box_container.h +++ b/scene/gui/box_container.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -35,16 +35,31 @@ class BoxContainer : public Container { OBJ_TYPE(BoxContainer,Container); +public: + + enum AlignMode { + ALIGN_BEGIN, + ALIGN_CENTER, + ALIGN_END + }; + +private: bool vertical; + AlignMode align; void _resort(); protected: void _notification(int p_what); + + static void _bind_methods(); public: void add_spacer(bool p_begin=false); + void set_alignment(AlignMode p_align); + AlignMode get_alignment() const; + virtual Size2 get_minimum_size() const; BoxContainer(bool p_vertical=false); @@ -73,4 +88,6 @@ public: VBoxContainer() : BoxContainer(true) {} }; +VARIANT_ENUM_CAST(BoxContainer::AlignMode); + #endif // BOX_CONTAINER_H diff --git a/scene/gui/button.cpp b/scene/gui/button.cpp index 0532ab22da..edeb18bfc1 100644 --- a/scene/gui/button.cpp +++ b/scene/gui/button.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -97,6 +97,13 @@ void Button::_notification(int p_what) { } break; } + + if (has_focus()) { + + Ref<StyleBox> style = get_stylebox("focus"); + style->draw(ci,Rect2(Point2(),size)); + } + Ref<StyleBox> style = get_stylebox("normal" ); Ref<Font> font=get_font("font"); Ref<Texture> _icon; @@ -115,6 +122,8 @@ void Button::_notification(int p_what) { text_ofs.y+=style->get_offset().y; } break; case ALIGN_CENTER: { + if (text_ofs.x<0) + text_ofs.x=0; text_ofs+=icon_ofs; text_ofs+=style->get_offset(); } break; @@ -132,11 +141,7 @@ void Button::_notification(int p_what) { _icon->draw(ci,Point2(style->get_offset().x, Math::floor( (size.height-_icon->get_height())/2.0 ) ),is_disabled()?Color(1,1,1,0.4):Color(1,1,1) ); } - if (has_focus()) { - Ref<StyleBox> style = get_stylebox("focus"); - style->draw(ci,Rect2(Point2(),size)); - } } } @@ -220,11 +225,11 @@ void Button::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_text_align"),&Button::get_text_align); ObjectTypeDB::bind_method(_MD("is_flat"),&Button::is_flat); - ADD_PROPERTY( PropertyInfo( Variant::STRING, "text", PROPERTY_HINT_NONE,"",PROPERTY_USAGE_DEFAULT_INTL ), _SCS("set_text"),_SCS("get_text") ); - ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture" ), _SCS("set_button_icon"),_SCS("get_button_icon") ); + ADD_PROPERTYNZ( PropertyInfo( Variant::STRING, "text", PROPERTY_HINT_NONE,"",PROPERTY_USAGE_DEFAULT_INTL ), _SCS("set_text"),_SCS("get_text") ); + ADD_PROPERTYNZ( PropertyInfo( Variant::OBJECT, "icon", PROPERTY_HINT_RESOURCE_TYPE, "Texture" ), _SCS("set_button_icon"),_SCS("get_button_icon") ); ADD_PROPERTY( PropertyInfo( Variant::BOOL, "flat" ), _SCS("set_flat"),_SCS("is_flat") ); - ADD_PROPERTY( PropertyInfo( Variant::BOOL, "clip_text" ), _SCS("set_clip_text"),_SCS("get_clip_text") ); - ADD_PROPERTY( PropertyInfo( Variant::INT, "align",PROPERTY_HINT_ENUM,"Left,Center,Right" ), _SCS("set_text_align"),_SCS("get_text_align") ); + ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "clip_text" ), _SCS("set_clip_text"),_SCS("get_clip_text") ); + ADD_PROPERTYNO( PropertyInfo( Variant::INT, "align",PROPERTY_HINT_ENUM,"Left,Center,Right" ), _SCS("set_text_align"),_SCS("get_text_align") ); } diff --git a/scene/gui/button.h b/scene/gui/button.h index cf79e23579..690179b90c 100644 --- a/scene/gui/button.h +++ b/scene/gui/button.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/button_array.cpp b/scene/gui/button_array.cpp index e3a2b7b290..b86e32dda7 100644 --- a/scene/gui/button_array.cpp +++ b/scene/gui/button_array.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,7 +34,7 @@ bool ButtonArray::_set(const StringName& p_name, const Variant& p_value) { String n=String(p_name); if (n.begins_with("button/")) { - String what = n.get_slice("/",1); + String what = n.get_slicec('/',1); if (what=="count") { int new_size=p_value; if (new_size>0 && buttons.size()==0) { @@ -57,7 +57,7 @@ bool ButtonArray::_set(const StringName& p_name, const Variant& p_value) { } else { int idx=what.to_int(); ERR_FAIL_INDEX_V(idx,buttons.size(),false); - String f = n.get_slice("/",2); + String f = n.get_slicec('/',2); if (f=="text") buttons[idx].text=p_value; else if (f=="icon") @@ -80,7 +80,7 @@ bool ButtonArray::_get(const StringName& p_name,Variant &r_ret) const { String n=String(p_name); if (n.begins_with("button/")) { - String what = n.get_slice("/",1); + String what = n.get_slicec('/',1); if (what=="count") { r_ret=buttons.size(); } else if (what=="align") { @@ -92,7 +92,7 @@ bool ButtonArray::_get(const StringName& p_name,Variant &r_ret) const { } else { int idx=what.to_int(); ERR_FAIL_INDEX_V(idx,buttons.size(),false); - String f = n.get_slice("/",2); + String f = n.get_slicec('/',2); if (f=="text") r_ret=buttons[idx].text; else if (f=="icon") diff --git a/scene/gui/button_array.h b/scene/gui/button_array.h index f536040039..ea2c1e4968 100644 --- a/scene/gui/button_array.h +++ b/scene/gui/button_array.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -26,98 +26,98 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef BUTTON_ARRAY_H
-#define BUTTON_ARRAY_H
-
-#include "scene/gui/control.h"
-
-class ButtonArray : public Control {
-
- OBJ_TYPE(ButtonArray, Control);
-public:
- enum Align {
- ALIGN_BEGIN,
- ALIGN_CENTER,
- ALIGN_END,
- ALIGN_FILL,
- ALIGN_EXPAND_FILL
- };
-private:
-
- Orientation orientation;
- Align align;
-
- struct Button {
-
- String text;
- Ref<Texture> icon;
- mutable int _ms_cache;
- mutable int _pos_cache;
- mutable int _size_cache;
- };
-
- int selected;
- int hover;
- double min_button_size;
-
- Vector<Button> buttons;
-protected:
-
- bool _set(const StringName& p_name, const Variant& p_value);
- bool _get(const StringName& p_name,Variant &r_ret) const;
- void _get_property_list( List<PropertyInfo> *p_list) const;
-
- void _notification(int p_what);
- static void _bind_methods();
-
-public:
-
- void _input_event(const InputEvent& p_event);
-
-
- void set_align(Align p_align);
- Align get_align() const;
-
- void add_button(const String& p_button);
- void add_icon_button(const Ref<Texture>& p_icon,const String& p_button="");
-
- void set_button_text(int p_button, const String& p_text);
- void set_button_icon(int p_button, const Ref<Texture>& p_icon);
-
-
- String get_button_text(int p_button) const;
- Ref<Texture> get_button_icon(int p_button) const;
-
- int get_selected() const;
- int get_hovered() const;
- void set_selected(int p_selected);
-
- int get_button_count() const;
-
- void erase_button(int p_button);
- void clear();
-
- virtual Size2 get_minimum_size() const;
-
- virtual void get_translatable_strings(List<String> *p_strings) const;
-
-
- ButtonArray(Orientation p_orientation=HORIZONTAL);
-};
-
-class HButtonArray : public ButtonArray {
- OBJ_TYPE(HButtonArray,ButtonArray);
-public:
-
- HButtonArray() : ButtonArray(HORIZONTAL) {};
-};
-
-class VButtonArray : public ButtonArray {
- OBJ_TYPE(VButtonArray,ButtonArray);
-public:
-
- VButtonArray() : ButtonArray(VERTICAL) {};
-};
-
-
-#endif // BUTTON_ARRAY_H
+#ifndef BUTTON_ARRAY_H +#define BUTTON_ARRAY_H + +#include "scene/gui/control.h" + +class ButtonArray : public Control { + + OBJ_TYPE(ButtonArray, Control); +public: + enum Align { + ALIGN_BEGIN, + ALIGN_CENTER, + ALIGN_END, + ALIGN_FILL, + ALIGN_EXPAND_FILL + }; +private: + + Orientation orientation; + Align align; + + struct Button { + + String text; + Ref<Texture> icon; + mutable int _ms_cache; + mutable int _pos_cache; + mutable int _size_cache; + }; + + int selected; + int hover; + double min_button_size; + + Vector<Button> buttons; +protected: + + bool _set(const StringName& p_name, const Variant& p_value); + bool _get(const StringName& p_name,Variant &r_ret) const; + void _get_property_list( List<PropertyInfo> *p_list) const; + + void _notification(int p_what); + static void _bind_methods(); + +public: + + void _input_event(const InputEvent& p_event); + + + void set_align(Align p_align); + Align get_align() const; + + void add_button(const String& p_button); + void add_icon_button(const Ref<Texture>& p_icon,const String& p_button=""); + + void set_button_text(int p_button, const String& p_text); + void set_button_icon(int p_button, const Ref<Texture>& p_icon); + + + String get_button_text(int p_button) const; + Ref<Texture> get_button_icon(int p_button) const; + + int get_selected() const; + int get_hovered() const; + void set_selected(int p_selected); + + int get_button_count() const; + + void erase_button(int p_button); + void clear(); + + virtual Size2 get_minimum_size() const; + + virtual void get_translatable_strings(List<String> *p_strings) const; + + + ButtonArray(Orientation p_orientation=HORIZONTAL); +}; + +class HButtonArray : public ButtonArray { + OBJ_TYPE(HButtonArray,ButtonArray); +public: + + HButtonArray() : ButtonArray(HORIZONTAL) {}; +}; + +class VButtonArray : public ButtonArray { + OBJ_TYPE(VButtonArray,ButtonArray); +public: + + VButtonArray() : ButtonArray(VERTICAL) {}; +}; + + +#endif // BUTTON_ARRAY_H diff --git a/scene/gui/button_group.cpp b/scene/gui/button_group.cpp index 94cc0a8d51..c92d7f2696 100644 --- a/scene/gui/button_group.cpp +++ b/scene/gui/button_group.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -155,6 +155,6 @@ void ButtonGroup::_bind_methods() { } -ButtonGroup::ButtonGroup() +ButtonGroup::ButtonGroup() : BoxContainer(true) { } diff --git a/scene/gui/button_group.h b/scene/gui/button_group.h index e154f609a8..74e847e937 100644 --- a/scene/gui/button_group.h +++ b/scene/gui/button_group.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,14 +29,14 @@ #ifndef BUTTON_GROUP_H #define BUTTON_GROUP_H -#include "scene/gui/control.h" +#include "scene/gui/box_container.h" class BaseButton; -class ButtonGroup : public Control { +class ButtonGroup : public BoxContainer { - OBJ_TYPE(ButtonGroup,Control); + OBJ_TYPE(ButtonGroup,BoxContainer); Set<BaseButton*> buttons; diff --git a/scene/gui/center_container.cpp b/scene/gui/center_container.cpp index 4f8f50781c..8a22a38980 100644 --- a/scene/gui/center_container.cpp +++ b/scene/gui/center_container.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/center_container.h b/scene/gui/center_container.h index 9cd9173fab..4d8d06ac8c 100644 --- a/scene/gui/center_container.h +++ b/scene/gui/center_container.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/empty_control.cpp b/scene/gui/check_box.cpp index 1e377b2b73..2aa82bc5f5 100644 --- a/scene/gui/empty_control.cpp +++ b/scene/gui/check_box.cpp @@ -1,5 +1,5 @@ /*************************************************************************/ -/* empty_control.cpp */ +/* check_button.cpp */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -26,34 +26,54 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#include "empty_control.h"
-
-Size2 EmptyControl::get_minimum_size() const {
-
- return minsize;
-}
-
-void EmptyControl::set_minsize(const Size2& p_size) {
-
- minsize=p_size;
- minimum_size_changed();
-}
-
-Size2 EmptyControl::get_minsize() const {
-
- return minsize;
-}
-
-
-void EmptyControl::_bind_methods() {
-
-
- ObjectTypeDB::bind_method(_MD("set_minsize","minsize"),&EmptyControl::set_minsize);
- ObjectTypeDB::bind_method(_MD("get_minsize"),&EmptyControl::get_minsize);
-
- ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"minsize"), _SCS("set_minsize"),_SCS("get_minsize") );
-}
-
-EmptyControl::EmptyControl()
-{
-}
+#include "check_box.h" + +#include "servers/visual_server.h" +#include "button_group.h" + + +void CheckBox::_notification(int p_what) { + + if (p_what==NOTIFICATION_DRAW) { + + RID ci = get_canvas_item(); + + Ref<Texture> on=Control::get_icon(is_radio() ? "radio_checked" : "checked"); + Ref<Texture> off=Control::get_icon(is_radio() ? "radio_unchecked" : "unchecked"); + + Vector2 ofs; + ofs.x = 0; + ofs.y = int((get_size().height - on->get_height())/2); + + if (is_pressed()) + on->draw(ci,ofs); + else + off->draw(ci,ofs); + + + } +} + +bool CheckBox::is_radio() +{ + Node* parent = this; + do { + parent = parent->get_parent(); + if (parent->cast_to<ButtonGroup>()) + break; + } while (parent); + + return (parent != 0); +} + +CheckBox::CheckBox(const String &p_text): + Button(p_text) +{ + set_toggle_mode(true); + set_text_align(ALIGN_LEFT); + +} + +CheckBox::~CheckBox() +{ +} diff --git a/scene/gui/empty_control.h b/scene/gui/check_box.h index 993af45ac4..171fd55351 100644 --- a/scene/gui/empty_control.h +++ b/scene/gui/check_box.h @@ -1,5 +1,5 @@ /*************************************************************************/ -/* empty_control.h */ +/* check_box.h */ /*************************************************************************/ /* This file is part of: */ /* GODOT ENGINE */ @@ -26,23 +26,30 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ -#ifndef EMPTY_CONTROL_H
-#define EMPTY_CONTROL_H
-
-#include "scene/gui/control.h"
-
-class EmptyControl : public Control {
-
- OBJ_TYPE(EmptyControl,Control);
- Size2 minsize;
-protected:
- static void _bind_methods();
-public:
- virtual Size2 get_minimum_size() const;
- void set_minsize(const Size2& p_size);
- Size2 get_minsize() const;
-
- EmptyControl();
-};
-
-#endif // EMPTY_CONTROL_H
+#ifndef CHECK_BOX_H +#define CHECK_BOX_H + + +#include "scene/gui/button.h" +/** +@author Mariano Suligoy <marianognu.esyrpg@gmail.com> +*/ +class CheckBox : public Button { + + OBJ_TYPE( CheckBox, Button ); + + +protected: + void _notification(int p_what); + + bool is_radio(); + + +public: + + CheckBox(const String& p_text=String()); + ~CheckBox(); + +}; + +#endif diff --git a/scene/gui/check_button.cpp b/scene/gui/check_button.cpp index 10dca67053..d765aefe5e 100644 --- a/scene/gui/check_button.cpp +++ b/scene/gui/check_button.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/check_button.h b/scene/gui/check_button.h index a4ebe5b8af..b90bb31c2d 100644 --- a/scene/gui/check_button.h +++ b/scene/gui/check_button.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 193649c815..c30d473610 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -352,6 +352,7 @@ void ColorPickerButton::set_color(const Color& p_color){ picker->set_color(p_color); + update(); } Color ColorPickerButton::get_color() const{ diff --git a/scene/gui/color_picker.h b/scene/gui/color_picker.h index e45b4b131e..0756e88cf2 100644 --- a/scene/gui/color_picker.h +++ b/scene/gui/color_picker.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/color_ramp_edit.cpp b/scene/gui/color_ramp_edit.cpp new file mode 100644 index 0000000000..14a48fe3d3 --- /dev/null +++ b/scene/gui/color_ramp_edit.cpp @@ -0,0 +1,422 @@ +#include "color_ramp_edit.h" +#include "os/keyboard.h" + +ColorRampEdit::ColorRampEdit(){ + grabbed=-1; + grabbing=false; + set_focus_mode(FOCUS_ALL); + + popup = memnew( PopupPanel ); + picker = memnew( ColorPicker ); + popup->add_child(picker); + popup->set_child_rect(picker); + add_child(popup); + + checker = Ref<ImageTexture>(memnew( ImageTexture )); + checker->create_from_image( Image(checker_bg_png),ImageTexture::FLAG_REPEAT ); +} + +int ColorRampEdit::_get_point_from_pos(int x) { + int result = -1; + int total_w = get_size().width-get_size().height-3; + for(int i=0;i<points.size();i++) { + //Check if we clicked at point + if (ABS(x-points[i].offset*total_w+1)<(POINT_WIDTH/2+1)) { + result=i; + } + } + return result; +} + +void ColorRampEdit::_show_color_picker() { + if (grabbed==-1) + return; + Size2 ms = Size2(350, picker->get_combined_minimum_size().height+10); + picker->set_color(points[grabbed].color); + popup->set_pos(get_global_pos()-Vector2(ms.width-get_size().width,ms.height)); + popup->set_size(ms); + popup->popup(); +} + +ColorRampEdit::~ColorRampEdit() { + +} + +void ColorRampEdit::_input_event(const InputEvent& p_event) { + + if (p_event.type==InputEvent::KEY && p_event.key.pressed && p_event.key.scancode==KEY_DELETE && grabbed!=-1) { + + points.remove(grabbed); + grabbed=-1; + grabbing=false; + update(); + emit_signal("ramp_changed"); + accept_event(); + } + + //Show color picker on double click. + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.doubleclick && p_event.mouse_button.pressed) { + grabbed=_get_point_from_pos(p_event.mouse_button.x); + _show_color_picker(); + accept_event(); + } + + //Delete point on right click + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==2 && p_event.mouse_button.pressed) { + grabbed=_get_point_from_pos(p_event.mouse_button.x); + if(grabbed != -1) + { + points.remove(grabbed); + grabbed=-1; + grabbing=false; + update(); + emit_signal("ramp_changed"); + accept_event(); + } + } + + //Hold alt key to duplicate selected color + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed && p_event.key.mod.alt ) { + + int x = p_event.mouse_button.x; + grabbed=_get_point_from_pos(x); + + if( grabbed != -1 ) { + int total_w = get_size().width-get_size().height-3; + ColorRamp::Point newPoint = points[grabbed]; + newPoint.offset=CLAMP(x/float(total_w),0,1); + + points.push_back(newPoint); + points.sort(); + for(int i=0;i<points.size();++i) { + if (points[i].offset==newPoint.offset) { + grabbed=i; + break; + } + } + + emit_signal("ramp_changed"); + update(); + } + } + + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && p_event.mouse_button.pressed) { + + update(); + int x = p_event.mouse_button.x; + int total_w = get_size().width-get_size().height-3; + + //Check if color selector was clicked. + if (x>total_w+3) { + _show_color_picker(); + return; + } + + grabbing=true; + + grabbed=_get_point_from_pos(x); + //grab or select + if (grabbed!=-1) { + return; + } + + //insert + ColorRamp::Point newPoint; + newPoint.offset=CLAMP(x/float(total_w),0,1); + + ColorRamp::Point prev; + ColorRamp::Point next; + + int pos=-1; + for(int i=0;i<points.size();i++) { + if (points[i].offset<newPoint.offset) + pos=i; + } + + if (pos==-1) { + + prev.color=Color(0,0,0); + prev.offset=0; + if (points.size()) { + next=points[0]; + } else { + next.color=Color(1,1,1); + next.offset=1.0; + } + } else { + + if (pos==points.size()-1) { + next.color=Color(1,1,1); + next.offset=1.0; + } else { + next=points[pos+1]; + } + prev=points[pos]; + + } + + newPoint.color=prev.color.linear_interpolate(next.color,(newPoint.offset-prev.offset)/(next.offset-prev.offset)); + + points.push_back(newPoint); + points.sort(); + for(int i=0;i<points.size();i++) { + if (points[i].offset==newPoint.offset) { + grabbed=i; + break; + } + } + + emit_signal("ramp_changed"); + + } + + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==1 && !p_event.mouse_button.pressed) { + + if (grabbing) { + grabbing=false; + emit_signal("ramp_changed"); + } + update(); + } + + if (p_event.type==InputEvent::MOUSE_MOTION && grabbing) { + + int total_w = get_size().width-get_size().height-3; + + int x = p_event.mouse_motion.x; + float newofs = CLAMP(x/float(total_w),0,1); + + //Snap to nearest point if holding shift + if (p_event.key.mod.shift) { + float snap_treshhold = 0.03; + float smallest_ofs = snap_treshhold; + bool founded = false; + int nearest_point; + for(int i=0;i<points.size();++i) { + if (i != grabbed) { + float temp_ofs = ABS(points[i].offset - newofs); + if (temp_ofs < smallest_ofs) { + smallest_ofs = temp_ofs; + nearest_point = i; + if (founded) + break; + founded = true; + } + } + } + if (founded) { + if (points[nearest_point].offset < newofs) + newofs = points[nearest_point].offset+0.00001; + else + newofs = points[nearest_point].offset-0.00001; + newofs = CLAMP(newofs,0,1); + } + } + + bool valid=true; + for(int i=0;i<points.size();i++) { + + if (points[i].offset==newofs && i!=grabbed) { + valid=false; + } + } + + if (!valid) + return; + + points[grabbed].offset=newofs; + + points.sort(); + for(int i=0;i<points.size();i++) { + if (points[i].offset==newofs) { + grabbed=i; + break; + } + } + + emit_signal("ramp_changed"); + + update(); + } +} + +void ColorRampEdit::_notification(int p_what) { + + if (p_what==NOTIFICATION_ENTER_TREE) { + picker->connect("color_changed",this,"_color_changed"); + } + if (p_what==NOTIFICATION_DRAW) { + + int w = get_size().x; + int h = get_size().y; + + if (w == 0 || h == 0) + return; //Safety check. We have division by 'h'. And in any case there is nothing to draw with such size + + int total_w = get_size().width-get_size().height-3; + + //Draw checker pattern for ramp + _draw_checker(0,0, total_w, h); + + //Draw color ramp + ColorRamp::Point prev; + prev.offset=0; + if(points.size() == 0) + prev.color=Color(0,0,0); //Draw black rectangle if we have no points + else + prev.color = points[0].color; //Extend color of first point to the beginning. + + for(int i=-1;i<points.size();i++) { + + ColorRamp::Point next; + //If there is no next point + if (i+1 == points.size()) { + if(points.size() == 0) + next.color=Color(0,0,0); //Draw black rectangle if we have no points + else + next.color=points[i].color; //Extend color of last point to the end. + next.offset=1; + } else { + next=points[i+1]; + } + + if (prev.offset==next.offset) { + prev=next; + continue; + } + + Vector<Vector2> points; + Vector<Color> colors; + points.push_back(Vector2(prev.offset*total_w,h)); + points.push_back(Vector2(prev.offset*total_w,0)); + points.push_back(Vector2(next.offset*total_w,0)); + points.push_back(Vector2(next.offset*total_w,h)); + colors.push_back(prev.color); + colors.push_back(prev.color); + colors.push_back(next.color); + colors.push_back(next.color); + draw_primitive(points,colors,Vector<Point2>()); + prev=next; + } + + //Draw point markers + for(int i=0;i<points.size();i++) { + + Color col = i==grabbed?Color(1,0.0,0.0,0.9):points[i].color.contrasted(); + col.a = 0.9; + + draw_line(Vector2(points[i].offset*total_w,0),Vector2(points[i].offset*total_w,h/2),col); + draw_rect(Rect2(points[i].offset*total_w-POINT_WIDTH/2, h/2, POINT_WIDTH, h/2), Color(0.6, 0.6, 0.6, i==grabbed?0.9:0.4)); + draw_line(Vector2(points[i].offset*total_w-POINT_WIDTH/2,h/2),Vector2(points[i].offset*total_w-POINT_WIDTH/2,h-1),col); + draw_line(Vector2(points[i].offset*total_w+POINT_WIDTH/2,h/2),Vector2(points[i].offset*total_w+POINT_WIDTH/2,h-1),col); + draw_line(Vector2(points[i].offset*total_w-POINT_WIDTH/2,h/2),Vector2(points[i].offset*total_w+POINT_WIDTH/2,h/2),col); + draw_line(Vector2(points[i].offset*total_w-POINT_WIDTH/2,h-1),Vector2(points[i].offset*total_w+POINT_WIDTH/2,h-1),col); + + } + + + //Draw "button" for color selector + _draw_checker(total_w+3,0, h, h); + if (grabbed!=-1) { + //Draw with selection color + draw_rect(Rect2(total_w+3,0,h,h),points[grabbed].color); + } else { + //if no color selected draw grey color with 'X' on top. + draw_rect(Rect2(total_w+3,0,h,h), Color(0.5, 0.5, 0.5, 1)); + draw_line(Vector2(total_w+3,0),Vector2(total_w+3+h,h),Color(1,1,1,0.6)); + draw_line(Vector2(total_w+3,h),Vector2(total_w+3+h,0),Color(1,1,1,0.6)); + } + + //Draw borders around color ramp if in focus + if (has_focus()) { + + draw_line(Vector2(-1,-1),Vector2(total_w+1,-1),Color(1,1,1,0.6)); + draw_line(Vector2(total_w+1,-1),Vector2(total_w+1,h+1),Color(1,1,1,0.6)); + draw_line(Vector2(total_w+1,h+1),Vector2(-1,h+1),Color(1,1,1,0.6)); + draw_line(Vector2(-1,-1),Vector2(-1,h+1),Color(1,1,1,0.6)); + } + + } +} + +void ColorRampEdit::_draw_checker(int x, int y, int w, int h) { + //Draw it with polygon to insert UVs for scale + Vector<Vector2> backPoints; + backPoints.push_back(Vector2(x, y)); + backPoints.push_back(Vector2(x, y+h)); + backPoints.push_back(Vector2(x+w, y+h)); + backPoints.push_back(Vector2(x+w, y)); + Vector<Color> colorPoints; + colorPoints.push_back(Color(1, 1, 1, 1)); + colorPoints.push_back(Color(1, 1, 1, 1)); + colorPoints.push_back(Color(1, 1, 1, 1)); + colorPoints.push_back(Color(1, 1, 1, 1)); + Vector<Vector2> uvPoints; + //Draw checker pattern pixel-perfect and scale it by 2. + uvPoints.push_back(Vector2(x, y)); + uvPoints.push_back(Vector2(x, y+h*.5f/checker->get_height())); + uvPoints.push_back(Vector2(x+w*.5f/checker->get_width(), y+h*.5f/checker->get_height())); + uvPoints.push_back(Vector2(x+w*.5f/checker->get_width(), y)); + draw_polygon(backPoints, colorPoints, uvPoints, checker); +} + +Size2 ColorRampEdit::get_minimum_size() const { + + return Vector2(0,16); +} + +void ColorRampEdit::_color_changed(const Color& p_color) { + + if (grabbed==-1) + return; + points[grabbed].color=p_color; + update(); + emit_signal("ramp_changed"); + +} + +void ColorRampEdit::set_ramp(const Vector<float>& p_offsets,const Vector<Color>& p_colors) { + + ERR_FAIL_COND(p_offsets.size()!=p_colors.size()); + points.clear(); + for(int i=0;i<p_offsets.size();i++) { + ColorRamp::Point p; + p.offset=p_offsets[i]; + p.color=p_colors[i]; + points.push_back(p); + } + + points.sort(); + update(); +} + +Vector<float> ColorRampEdit::get_offsets() const { + Vector<float> ret; + for(int i=0;i<points.size();i++) + ret.push_back(points[i].offset); + return ret; +} + +Vector<Color> ColorRampEdit::get_colors() const { + Vector<Color> ret; + for(int i=0;i<points.size();i++) + ret.push_back(points[i].color); + return ret; +} + +void ColorRampEdit::set_points(Vector<ColorRamp::Point>& p_points) { + if(points.size() != p_points.size()) + grabbed = -1; + points.clear(); + points = p_points; +} + +Vector<ColorRamp::Point>& ColorRampEdit::get_points() { + return points; +} + +void ColorRampEdit::_bind_methods() { + ObjectTypeDB::bind_method(_MD("_input_event"),&ColorRampEdit::_input_event); + ObjectTypeDB::bind_method(_MD("_color_changed"),&ColorRampEdit::_color_changed); + ADD_SIGNAL(MethodInfo("ramp_changed")); +} diff --git a/scene/gui/color_ramp_edit.h b/scene/gui/color_ramp_edit.h new file mode 100644 index 0000000000..91292eed0d --- /dev/null +++ b/scene/gui/color_ramp_edit.h @@ -0,0 +1,52 @@ +#ifndef SCENE_GUI_COLOR_RAMP_EDIT_H_ +#define SCENE_GUI_COLOR_RAMP_EDIT_H_ + +#include "scene/gui/popup.h" +#include "scene/gui/color_picker.h" +#include "scene/resources/color_ramp.h" +#include "scene/resources/default_theme/theme_data.h" + +#define POINT_WIDTH 8 + +class ColorRampEdit : public Control { + + OBJ_TYPE(ColorRampEdit,Control); + + PopupPanel *popup; + ColorPicker *picker; + + Ref<ImageTexture> checker; + + bool grabbing; + int grabbed; + Vector<ColorRamp::Point> points; + + void _draw_checker(int x, int y, int w, int h); + void _color_changed(const Color& p_color); + int _get_point_from_pos(int x); + void _show_color_picker(); + +protected: + void _input_event(const InputEvent& p_event); + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_ramp(const Vector<float>& p_offsets,const Vector<Color>& p_colors); + Vector<float> get_offsets() const; + Vector<Color> get_colors() const; + void set_points(Vector<ColorRamp::Point>& p_points); + Vector<ColorRamp::Point>& get_points(); + virtual Size2 get_minimum_size() const; + + ColorRampEdit(); + virtual ~ColorRampEdit(); +}; + +/*class ColorRampEditPanel : public Panel +{ + OBJ_TYPE(ColorRampEditPanel, Panel ); +};*/ + + +#endif /* SCENE_GUI_COLOR_RAMP_EDIT_H_ */ diff --git a/scene/gui/container.cpp b/scene/gui/container.cpp index bb61723acf..6c74bc3977 100644 --- a/scene/gui/container.cpp +++ b/scene/gui/container.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/container.h b/scene/gui/container.h index 841244fb9a..ba9bf2d60f 100644 --- a/scene/gui/container.h +++ b/scene/gui/container.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/control.cpp b/scene/gui/control.cpp index 5a0706f01e..bd6b8078ff 100644 --- a/scene/gui/control.cpp +++ b/scene/gui/control.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -136,44 +136,54 @@ bool Control::_set(const StringName& p_name, const Variant& p_value) { if (p_value.get_type()==Variant::NIL) { if (name.begins_with("custom_icons/")) { - String dname = name.get_slice("/",1); + String dname = name.get_slicec('/',1); data.icon_override.erase(dname); + notification(NOTIFICATION_THEME_CHANGED); update(); } else if (name.begins_with("custom_styles/")) { - String dname = name.get_slice("/",1); + String dname = name.get_slicec('/',1); data.style_override.erase(dname); + notification(NOTIFICATION_THEME_CHANGED); update(); } else if (name.begins_with("custom_fonts/")) { - String dname = name.get_slice("/",1); + String dname = name.get_slicec('/',1); data.font_override.erase(dname); + notification(NOTIFICATION_THEME_CHANGED); update(); } else if (name.begins_with("custom_colors/")) { - String dname = name.get_slice("/",1); + String dname = name.get_slicec('/',1); data.color_override.erase(dname); + notification(NOTIFICATION_THEME_CHANGED); update(); } else if (name.begins_with("custom_constants/")) { - String dname = name.get_slice("/",1); + String dname = name.get_slicec('/',1); data.constant_override.erase(dname); + notification(NOTIFICATION_THEME_CHANGED); update(); } else return false; } else { if (name.begins_with("custom_icons/")) { - String dname = name.get_slice("/",1); + String dname = name.get_slicec('/',1); + notification(NOTIFICATION_THEME_CHANGED); add_icon_override(dname,p_value); } else if (name.begins_with("custom_styles/")) { - String dname = name.get_slice("/",1); + String dname = name.get_slicec('/',1); add_style_override(dname,p_value); + notification(NOTIFICATION_THEME_CHANGED); } else if (name.begins_with("custom_fonts/")) { - String dname = name.get_slice("/",1); + String dname = name.get_slicec('/',1); add_font_override(dname,p_value); + notification(NOTIFICATION_THEME_CHANGED); } else if (name.begins_with("custom_colors/")) { - String dname = name.get_slice("/",1); + String dname = name.get_slicec('/',1); add_color_override(dname,p_value); + notification(NOTIFICATION_THEME_CHANGED); } else if (name.begins_with("custom_constants/")) { - String dname = name.get_slice("/",1); + String dname = name.get_slicec('/',1); add_constant_override(dname,p_value); + notification(NOTIFICATION_THEME_CHANGED); } else return false; } @@ -207,22 +217,22 @@ bool Control::_get(const StringName& p_name,Variant &r_ret) const { return false; if (sname.begins_with("custom_icons/")) { - String name = sname.get_slice("/",1); + String name = sname.get_slicec('/',1); r_ret= data.icon_override.has(name)?Variant(data.icon_override[name]):Variant(); } else if (sname.begins_with("custom_styles/")) { - String name = sname.get_slice("/",1); + String name = sname.get_slicec('/',1); r_ret= data.style_override.has(name)?Variant(data.style_override[name]):Variant(); } else if (sname.begins_with("custom_fonts/")) { - String name = sname.get_slice("/",1); + String name = sname.get_slicec('/',1); r_ret= data.font_override.has(name)?Variant(data.font_override[name]):Variant(); } else if (sname.begins_with("custom_colors/")) { - String name = sname.get_slice("/",1); + String name = sname.get_slicec('/',1); r_ret= data.color_override.has(name)?Variant(data.color_override[name]):Variant(); } else if (sname.begins_with("custom_constants/")) { - String name = sname.get_slice("/",1); + String name = sname.get_slicec('/',1); r_ret= data.constant_override.has(name)?Variant(data.constant_override[name]):Variant(); } else @@ -514,13 +524,15 @@ void Control::_notification(int p_notification) { if (data.MI) { - data.window->window->modal_stack.erase(data.MI); + if (data.window && data.window->window) + data.window->window->modal_stack.erase(data.MI); data.MI=NULL; } if (data.SI) { //erase from subwindows - data.window->window->subwindows.erase(data.SI); + if (data.window && data.window->window) + data.window->window->subwindows.erase(data.SI); data.SI=NULL; } @@ -536,15 +548,18 @@ void Control::_notification(int p_notification) { Control * parent = get_parent()->cast_to<Control>(); //make children reference them theme - if (parent && data.theme.is_null() && parent->data.theme_owner) + + if (parent && data.theme.is_null() && parent->data.theme_owner) { _propagate_theme_changed(parent->data.theme_owner); + } } break; case NOTIFICATION_UNPARENTED: { //make children unreference the theme - if (data.theme.is_null() && data.theme_owner) + if (data.theme.is_null() && data.theme_owner) { _propagate_theme_changed(NULL); + } } break; case NOTIFICATION_MOVED_IN_PARENT: { @@ -940,67 +955,67 @@ void Control::_window_input_event(InputEvent p_event) { case InputEvent::MOUSE_BUTTON: { - window->key_event_accepted=false; + window->key_event_accepted=false; - Point2 mpos =(get_canvas_transform()).affine_inverse().xform(Point2(p_event.mouse_button.x,p_event.mouse_button.y)); - if (p_event.mouse_button.pressed) { + Point2 mpos =(get_canvas_transform()).affine_inverse().xform(Point2(p_event.mouse_button.x,p_event.mouse_button.y)); + if (p_event.mouse_button.pressed) { - Size2 pos = mpos; - if (window->mouse_focus && p_event.mouse_button.button_index!=window->mouse_focus_button) { + Size2 pos = mpos; + if (window->mouse_focus && p_event.mouse_button.button_index!=window->mouse_focus_button) { - //do not steal mouse focus and stuff + //do not steal mouse focus and stuff - } else { + } else { - _window_sort_modal_stack(); - while (!window->modal_stack.empty()) { + _window_sort_modal_stack(); + while (!window->modal_stack.empty()) { - Control *top = window->modal_stack.back()->get(); - if (!top->has_point(top->get_global_transform().affine_inverse().xform(pos))) { + Control *top = window->modal_stack.back()->get(); + if (!top->has_point(top->get_global_transform().affine_inverse().xform(pos))) { - if (top->data.modal_exclusive) { - //cancel event, sorry, modal exclusive EATS UP ALL - get_tree()->call_group(SceneTree::GROUP_CALL_REALTIME,"windows","_cancel_input_ID",p_event.ID); - get_tree()->set_input_as_handled(); - return; // no one gets the event if exclusive NO ONE - } + if (top->data.modal_exclusive) { + //cancel event, sorry, modal exclusive EATS UP ALL + get_tree()->call_group(SceneTree::GROUP_CALL_REALTIME,"windows","_cancel_input_ID",p_event.ID); + get_tree()->set_input_as_handled(); + return; // no one gets the event if exclusive NO ONE + } - top->notification(NOTIFICATION_MODAL_CLOSE); - top->_modal_stack_remove(); - top->hide(); - } else { - break; + top->notification(NOTIFICATION_MODAL_CLOSE); + top->_modal_stack_remove(); + top->hide(); + } else { + break; + } } - } - Matrix32 parent_xform; + Matrix32 parent_xform; - if (data.parent_canvas_item) - parent_xform=data.parent_canvas_item->get_global_transform(); + if (data.parent_canvas_item) + parent_xform=data.parent_canvas_item->get_global_transform(); - window->mouse_focus = _find_control_at_pos(this,pos,parent_xform,window->focus_inv_xform); - //print_line("has mf "+itos(window->mouse_focus!=NULL)); - window->mouse_focus_button=p_event.mouse_button.button_index; + window->mouse_focus = _find_control_at_pos(this,pos,parent_xform,window->focus_inv_xform); + //print_line("has mf "+itos(window->mouse_focus!=NULL)); + window->mouse_focus_button=p_event.mouse_button.button_index; - if (!window->mouse_focus) { - break; - } + if (!window->mouse_focus) { + break; + } - if (p_event.mouse_button.button_index==BUTTON_LEFT) { - window->drag_accum=Vector2(); - window->drag_attempted=false; - window->drag_data=Variant(); - } + if (p_event.mouse_button.button_index==BUTTON_LEFT) { + window->drag_accum=Vector2(); + window->drag_attempted=false; + window->drag_data=Variant(); + } - } + } p_event.mouse_button.global_x = pos.x; p_event.mouse_button.global_y = pos.y; @@ -1020,8 +1035,8 @@ void Control::_window_input_event(InputEvent p_event) { /*if (bool(GLOBAL_DEF("debug/print_clicked_control",false))) { - print_line(String(window->mouse_focus->get_path())+" - "+pos); - }*/ + print_line(String(window->mouse_focus->get_path())+" - "+pos); + }*/ #endif if (window->mouse_focus->get_focus_mode()!=FOCUS_NONE && window->mouse_focus!=window->key_focus && p_event.mouse_button.button_index==BUTTON_LEFT) { @@ -1033,10 +1048,12 @@ void Control::_window_input_event(InputEvent p_event) { if (window->mouse_focus->can_process()) { _window_call_input(window->mouse_focus,p_event); } - + get_tree()->call_group(SceneTree::GROUP_CALL_REALTIME,"windows","_cancel_input_ID",p_event.ID); get_tree()->set_input_as_handled(); + window->tooltip_popup->hide(); + } else { if (window->drag_preview && p_event.mouse_button.button_index==BUTTON_LEFT) { @@ -1126,6 +1143,7 @@ void Control::_window_input_event(InputEvent p_event) { over = _find_control_at_pos(this,pos,parent_xform,window->focus_inv_xform); } + if (window->drag_data.get_type()==Variant::NIL && over && !window->modal_stack.empty()) { Control *top = window->modal_stack.back()->get(); @@ -2266,7 +2284,10 @@ void Control::_window_sort_subwindows() { if (!window->subwindow_order_dirty) return; + window->modal_stack.sort_custom<CComparator>(); + window->subwindows.sort_custom<CComparator>(); + window->subwindow_order_dirty=false; } @@ -2682,8 +2703,29 @@ bool Control::is_stopping_mouse() const { Control *Control::get_focus_owner() const { ERR_FAIL_COND_V(!is_inside_tree(),NULL); - ERR_FAIL_COND_V(!window,NULL); - return window->key_focus; + ERR_FAIL_COND_V(!data.window,NULL); + return data.window->window->key_focus; +} + + +void Control::warp_mouse(const Point2& p_to_pos) { + ERR_FAIL_COND(!is_inside_tree()); + get_viewport()->warp_mouse(get_global_transform().xform(p_to_pos)); +} + + +bool Control::is_text_field() const { +/* + if (get_script_instance()) { + Variant v=p_point; + const Variant *p[2]={&v,&p_data}; + Variant::CallError ce; + Variant ret = get_script_instance()->call("is_text_field",p,2,ce); + if (ce.error==Variant::CallError::CALL_OK) + return ret; + } + */ + return false; } void Control::_bind_methods() { @@ -2782,6 +2824,9 @@ void Control::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_drag_preview","control:Control"),&Control::set_drag_preview); + ObjectTypeDB::bind_method(_MD("warp_mouse","to_pos"),&Control::warp_mouse); + + BIND_VMETHOD(MethodInfo("_input_event",PropertyInfo(Variant::INPUT_EVENT,"event"))); BIND_VMETHOD(MethodInfo(Variant::VECTOR2,"get_minimum_size")); BIND_VMETHOD(MethodInfo(Variant::OBJECT,"get_drag_data",PropertyInfo(Variant::VECTOR2,"pos"))); @@ -2802,16 +2847,16 @@ void Control::_bind_methods() { ADD_PROPERTYNZ( PropertyInfo(Variant::VECTOR2,"rect/size", PROPERTY_HINT_NONE, "",PROPERTY_USAGE_EDITOR), _SCS("set_size"),_SCS("get_size") ); ADD_PROPERTYNZ( PropertyInfo(Variant::VECTOR2,"rect/min_size"), _SCS("set_custom_minimum_size"),_SCS("get_custom_minimum_size") ); ADD_PROPERTYNZ( PropertyInfo(Variant::STRING,"hint/tooltip", PROPERTY_HINT_MULTILINE_TEXT), _SCS("set_tooltip"),_SCS("_get_tooltip") ); - ADD_PROPERTYI( PropertyInfo(Variant::NODE_PATH,"focus_neighbour/left" ), _SCS("set_focus_neighbour"),_SCS("get_focus_neighbour"),MARGIN_LEFT ); - ADD_PROPERTYI( PropertyInfo(Variant::NODE_PATH,"focus_neighbour/top" ), _SCS("set_focus_neighbour"),_SCS("get_focus_neighbour"),MARGIN_TOP ); - ADD_PROPERTYI( PropertyInfo(Variant::NODE_PATH,"focus_neighbour/right" ), _SCS("set_focus_neighbour"),_SCS("get_focus_neighbour"),MARGIN_RIGHT ); - ADD_PROPERTYI( PropertyInfo(Variant::NODE_PATH,"focus_neighbour/bottom" ), _SCS("set_focus_neighbour"),_SCS("get_focus_neighbour"),MARGIN_BOTTOM ); + ADD_PROPERTYINZ( PropertyInfo(Variant::NODE_PATH,"focus_neighbour/left" ), _SCS("set_focus_neighbour"),_SCS("get_focus_neighbour"),MARGIN_LEFT ); + ADD_PROPERTYINZ( PropertyInfo(Variant::NODE_PATH,"focus_neighbour/top" ), _SCS("set_focus_neighbour"),_SCS("get_focus_neighbour"),MARGIN_TOP ); + ADD_PROPERTYINZ( PropertyInfo(Variant::NODE_PATH,"focus_neighbour/right" ), _SCS("set_focus_neighbour"),_SCS("get_focus_neighbour"),MARGIN_RIGHT ); + ADD_PROPERTYINZ( PropertyInfo(Variant::NODE_PATH,"focus_neighbour/bottom" ), _SCS("set_focus_neighbour"),_SCS("get_focus_neighbour"),MARGIN_BOTTOM ); ADD_PROPERTY( PropertyInfo(Variant::BOOL,"focus/ignore_mouse"), _SCS("set_ignore_mouse"),_SCS("is_ignoring_mouse") ); ADD_PROPERTY( PropertyInfo(Variant::BOOL,"focus/stop_mouse"), _SCS("set_stop_mouse"),_SCS("is_stopping_mouse") ); ADD_PROPERTYNZ( PropertyInfo(Variant::INT,"size_flags/horizontal", PROPERTY_HINT_FLAGS, "Expand,Fill"), _SCS("set_h_size_flags"),_SCS("get_h_size_flags") ); ADD_PROPERTYNZ( PropertyInfo(Variant::INT,"size_flags/vertical", PROPERTY_HINT_FLAGS, "Expand,Fill"), _SCS("set_v_size_flags"),_SCS("get_v_size_flags") ); - ADD_PROPERTY( PropertyInfo(Variant::INT,"size_flags/stretch_ratio", PROPERTY_HINT_RANGE, "1,128,0.01"), _SCS("set_stretch_ratio"),_SCS("get_stretch_ratio") ); + ADD_PROPERTYNO( PropertyInfo(Variant::INT,"size_flags/stretch_ratio", PROPERTY_HINT_RANGE, "1,128,0.01"), _SCS("set_stretch_ratio"),_SCS("get_stretch_ratio") ); ADD_PROPERTYNZ( PropertyInfo(Variant::OBJECT,"theme/theme", PROPERTY_HINT_RESOURCE_TYPE, "Theme"), _SCS("set_theme"),_SCS("get_theme") ); BIND_CONSTANT( ANCHOR_BEGIN ); @@ -2854,7 +2899,7 @@ void Control::_bind_methods() { BIND_CONSTANT( SIZE_EXPAND_FILL ); ADD_SIGNAL( MethodInfo("resized") ); - ADD_SIGNAL( MethodInfo("input_event") ); + ADD_SIGNAL( MethodInfo("input_event",PropertyInfo(Variant::INPUT_EVENT,"ev")) ); ADD_SIGNAL( MethodInfo("mouse_enter") ); ADD_SIGNAL( MethodInfo("mouse_exit") ); ADD_SIGNAL( MethodInfo("focus_enter") ); diff --git a/scene/gui/control.h b/scene/gui/control.h index 64b5a9b661..4311b299c8 100644 --- a/scene/gui/control.h +++ b/scene/gui/control.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -380,7 +380,9 @@ public: void grab_click_focus(); + void warp_mouse(const Point2& p_to_pos); + virtual bool is_text_field() const; Control(); ~Control(); diff --git a/scene/gui/custom_button.cpp b/scene/gui/custom_button.cpp index ed3f01e5fa..53a3bf0914 100644 --- a/scene/gui/custom_button.cpp +++ b/scene/gui/custom_button.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/custom_button.h b/scene/gui/custom_button.h index 1f0f0470ed..49fcf7408f 100644 --- a/scene/gui/custom_button.h +++ b/scene/gui/custom_button.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/dialogs.cpp b/scene/gui/dialogs.cpp index a82cfc7ea6..0c0f924f52 100644 --- a/scene/gui/dialogs.cpp +++ b/scene/gui/dialogs.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -274,7 +274,7 @@ Button* AcceptDialog::add_button(const String& p_text,bool p_right,const String& } if (p_action!="") { - button->connect("pressed",this,"_custom_action",make_binds(p_action)); + button->connect("pressed",this,"_custom_action",varray(p_action)); } return button; @@ -328,8 +328,8 @@ AcceptDialog::AcceptDialog() { label->set_anchor(MARGIN_RIGHT,ANCHOR_END); label->set_anchor(MARGIN_BOTTOM,ANCHOR_END); label->set_begin( Point2( margin, margin) ); - label->set_end( Point2( margin, button_margin) ); - label->set_autowrap(true); + label->set_end( Point2( margin, button_margin+10) ); + //label->set_autowrap(true); add_child(label); hbc = memnew( HBoxContainer ); diff --git a/scene/gui/dialogs.h b/scene/gui/dialogs.h index e547d5f2af..67c574a420 100644 --- a/scene/gui/dialogs.h +++ b/scene/gui/dialogs.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/file_dialog.cpp b/scene/gui/file_dialog.cpp index a7ff1431bd..8e428fd71c 100644 --- a/scene/gui/file_dialog.cpp +++ b/scene/gui/file_dialog.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,6 +29,8 @@ #include "file_dialog.h" #include "scene/gui/label.h" #include "print_string.h" +#include "os/keyboard.h" + FileDialog::GetIconFunc FileDialog::get_icon_func=NULL; @@ -90,7 +92,6 @@ void FileDialog::_file_entered(const String& p_file) { } void FileDialog::_save_confirm_pressed() { - String f=dir_access->get_current_dir().plus_file(file->get_text()); emit_signal("file_selected",f); hide(); @@ -156,7 +157,6 @@ void FileDialog::_action_pressed() { if (mode==MODE_SAVE_FILE) { - String ext = f.extension(); bool valid=false; if (filter->get_selected()==filter->get_item_count()-1) { @@ -184,7 +184,8 @@ void FileDialog::_action_pressed() { if (idx>=0 && idx<filters.size()) { String flt=filters[idx].get_slice(";",0); - for (int j=0;j<flt.get_slice_count(",");j++) { + int filterSliceCount=flt.get_slice_count(","); + for (int j=0;j<filterSliceCount;j++) { String str = (flt.get_slice(",",j).strip_edges()); if (f.match(str)) { @@ -192,6 +193,13 @@ void FileDialog::_action_pressed() { break; } } + + if (!valid && filterSliceCount>0) { + String str = (flt.get_slice(",",0).strip_edges()); + f+=str.substr(1, str.length()-1); + file->set_text(f.get_file()); + valid=true; + } } else { valid=true; } @@ -271,13 +279,20 @@ void FileDialog::update_file_list() { List<String> dirs; bool isdir; + bool ishidden; + bool show_hidden = show_hidden_files; String item; + while ((item=dir_access->get_next(&isdir))!="") { - - if (!isdir) - files.push_back(item); - else - dirs.push_back(item); + + ishidden = dir_access->current_is_hidden(); + + if (show_hidden || !ishidden) { + if (!isdir) + files.push_back(item); + else + dirs.push_back(item); + } } dirs.sort_custom<NoCaseComparator>(); @@ -593,22 +608,19 @@ void FileDialog::_update_drives() { drives->clear(); drives->show(); - int current=-1; - String abspath = dir_access->get_current_dir(); - for(int i=0;i<dir_access->get_drive_count();i++) { - String d = dir_access->get_drive(i); - if (abspath.begins_with(d)) - current=i; + String d = dir_access->get_drive(i); drives->add_item(dir_access->get_drive(i)); } - if (current!=-1) - drives->select(current); + drives->select(dir_access->get_current_drive()); } } +bool FileDialog::default_show_hidden_files=true; + + void FileDialog::_bind_methods() { ObjectTypeDB::bind_method(_MD("_tree_selected"),&FileDialog::_tree_selected); @@ -633,6 +645,8 @@ void FileDialog::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_vbox:VBoxContainer"),&FileDialog::get_vbox); ObjectTypeDB::bind_method(_MD("set_access","access"),&FileDialog::set_access); ObjectTypeDB::bind_method(_MD("get_access"),&FileDialog::get_access); + ObjectTypeDB::bind_method(_MD("set_show_hidden_files"),&FileDialog::set_show_hidden_files); + ObjectTypeDB::bind_method(_MD("is_showing_hidden_files"),&FileDialog::is_showing_hidden_files); ObjectTypeDB::bind_method(_MD("_select_drive"),&FileDialog::_select_drive); ObjectTypeDB::bind_method(_MD("_make_dir"),&FileDialog::_make_dir); ObjectTypeDB::bind_method(_MD("_make_dir_confirm"),&FileDialog::_make_dir_confirm); @@ -657,9 +671,23 @@ void FileDialog::_bind_methods() { } +void FileDialog::set_show_hidden_files(bool p_show) { + show_hidden_files=p_show; + invalidate(); +} + +bool FileDialog::is_showing_hidden_files() const { + return show_hidden_files; +} + +void FileDialog::set_default_show_hidden_files(bool p_show) { + default_show_hidden_files=p_show; +} FileDialog::FileDialog() { - + + show_hidden_files=default_show_hidden_files; + VBoxContainer *vbc = memnew( VBoxContainer ); add_child(vbc); set_child_rect(vbc); diff --git a/scene/gui/file_dialog.h b/scene/gui/file_dialog.h index bda1797696..ec42c7744a 100644 --- a/scene/gui/file_dialog.h +++ b/scene/gui/file_dialog.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -89,6 +89,10 @@ private: Vector<String> filters; + + static bool default_show_hidden_files; + bool show_hidden_files; + bool invalidated; void update_dir(); @@ -141,6 +145,11 @@ public: void set_access(Access p_access); Access get_access() const; + void set_show_hidden_files(bool p_show); + bool is_showing_hidden_files() const; + + static void set_default_show_hidden_files(bool p_show); + void invalidate(); FileDialog(); diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp new file mode 100644 index 0000000000..deb3151798 --- /dev/null +++ b/scene/gui/graph_edit.cpp @@ -0,0 +1,773 @@ +#include "graph_edit.h" +#include "os/input.h" +#include "os/keyboard.h" +bool GraphEditFilter::has_point(const Point2& p_point) const { + + return ge->_filter_input(p_point); +} + + +GraphEditFilter::GraphEditFilter(GraphEdit *p_edit) { + + ge=p_edit; +} + + +Error GraphEdit::connect_node(const StringName& p_from, int p_from_port,const StringName& p_to,int p_to_port) { + + if (is_node_connected(p_from,p_from_port,p_to,p_to_port)) + return OK; + Connection c; + c.from=p_from; + c.from_port=p_from_port; + c.to=p_to; + c.to_port=p_to_port; + connections.push_back(c); + top_layer->update(); + + return OK; +} + +bool GraphEdit::is_node_connected(const StringName& p_from, int p_from_port,const StringName& p_to,int p_to_port) { + + for(List<Connection>::Element *E=connections.front();E;E=E->next()) { + + if (E->get().from==p_from && E->get().from_port==p_from_port && E->get().to==p_to && E->get().to_port==p_to_port) + return true; + } + + return false; + +} + +void GraphEdit::disconnect_node(const StringName& p_from, int p_from_port,const StringName& p_to,int p_to_port){ + + + for(List<Connection>::Element *E=connections.front();E;E=E->next()) { + + if (E->get().from==p_from && E->get().from_port==p_from_port && E->get().to==p_to && E->get().to_port==p_to_port) { + + connections.erase(E); + top_layer->update(); + return; + } + } +} + +void GraphEdit::get_connection_list(List<Connection> *r_connections) const { + + *r_connections=connections; +} + + +void GraphEdit::_scroll_moved(double) { + + + _update_scroll_offset(); + top_layer->update(); +} + +void GraphEdit::_update_scroll_offset() { + + for(int i=0;i<get_child_count();i++) { + + GraphNode *gn=get_child(i)->cast_to<GraphNode>(); + if (!gn) + continue; + + Point2 pos=gn->get_offset(); + pos-=Point2(h_scroll->get_val(),v_scroll->get_val()); + gn->set_pos(pos); + } + +} + +void GraphEdit::_update_scroll() { + + if (updating) + return; + + updating=true; + Rect2 screen; + for(int i=0;i<get_child_count();i++) { + + GraphNode *gn=get_child(i)->cast_to<GraphNode>(); + if (!gn) + continue; + + Rect2 r; + r.pos=gn->get_offset(); + r.size=gn->get_size(); + screen = screen.merge(r); + } + + screen.pos-=get_size(); + screen.size+=get_size()*2.0; + + + h_scroll->set_min(screen.pos.x); + h_scroll->set_max(screen.pos.x+screen.size.x); + h_scroll->set_page(get_size().x); + if (h_scroll->get_max() - h_scroll->get_min() <= h_scroll->get_page()) + h_scroll->hide(); + else + h_scroll->show(); + + v_scroll->set_min(screen.pos.y); + v_scroll->set_max(screen.pos.y+screen.size.y); + v_scroll->set_page(get_size().y); + + if (v_scroll->get_max() - v_scroll->get_min() <= v_scroll->get_page()) + v_scroll->hide(); + else + v_scroll->show(); + + _update_scroll_offset(); + updating=false; +} + + +void GraphEdit::_graph_node_raised(Node* p_gn) { + + GraphNode *gn=p_gn->cast_to<GraphNode>(); + ERR_FAIL_COND(!gn); + gn->raise(); + top_layer->raise(); + +} + + +void GraphEdit::_graph_node_moved(Node *p_gn) { + + GraphNode *gn=p_gn->cast_to<GraphNode>(); + ERR_FAIL_COND(!gn); + top_layer->update(); +} + +void GraphEdit::add_child_notify(Node *p_child) { + + top_layer->call_deferred("raise"); //top layer always on top! + GraphNode *gn = p_child->cast_to<GraphNode>(); + if (gn) { + gn->connect("offset_changed",this,"_graph_node_moved",varray(gn)); + gn->connect("raise_request",this,"_graph_node_raised",varray(gn)); + _graph_node_moved(gn); + gn->set_stop_mouse(false); + } +} + +void GraphEdit::remove_child_notify(Node *p_child) { + + top_layer->call_deferred("raise"); //top layer always on top! + GraphNode *gn = p_child->cast_to<GraphNode>(); + if (gn) { + gn->disconnect("offset_changed",this,"_graph_node_moved"); + gn->disconnect("raise_request",this,"_graph_node_raised"); + } +} + +void GraphEdit::_notification(int p_what) { + + if (p_what==NOTIFICATION_READY) { + Size2 hmin = h_scroll->get_combined_minimum_size(); + Size2 vmin = v_scroll->get_combined_minimum_size(); + + v_scroll->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_END,vmin.width); + v_scroll->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,0); + v_scroll->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,0); + v_scroll->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,0); + + h_scroll->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_BEGIN,0); + h_scroll->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,0); + h_scroll->set_anchor_and_margin(MARGIN_TOP,ANCHOR_END,hmin.height); + h_scroll->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,0); + + } + if (p_what==NOTIFICATION_DRAW) { + VS::get_singleton()->canvas_item_set_clip(get_canvas_item(),true); + + } + + if (p_what==NOTIFICATION_RESIZED) { + _update_scroll(); + top_layer->update(); + } +} + +bool GraphEdit::_filter_input(const Point2& p_point) { + + Ref<Texture> port =get_icon("port","GraphNode"); + + float grab_r=port->get_width()*0.5; + for(int i=get_child_count()-1;i>=0;i--) { + + GraphNode *gn=get_child(i)->cast_to<GraphNode>(); + if (!gn) + continue; + + for(int j=0;j<gn->get_connection_output_count();j++) { + + Vector2 pos = gn->get_connection_output_pos(j)+gn->get_pos(); + if (pos.distance_to(p_point)<grab_r) + return true; + + + } + + for(int j=0;j<gn->get_connection_input_count();j++) { + + Vector2 pos = gn->get_connection_input_pos(j)+gn->get_pos(); + if (pos.distance_to(p_point)<grab_r) + return true; + + + } + + } + + return false; +} + +void GraphEdit::_top_layer_input(const InputEvent& p_ev) { + + if (p_ev.type==InputEvent::MOUSE_BUTTON && p_ev.mouse_button.button_index==BUTTON_LEFT && p_ev.mouse_button.pressed) { + + Ref<Texture> port =get_icon("port","GraphNode"); + Vector2 mpos(p_ev.mouse_button.x,p_ev.mouse_button.y); + float grab_r=port->get_width()*0.5; + for(int i=get_child_count()-1;i>=0;i--) { + + GraphNode *gn=get_child(i)->cast_to<GraphNode>(); + if (!gn) + continue; + + for(int j=0;j<gn->get_connection_output_count();j++) { + + Vector2 pos = gn->get_connection_output_pos(j)+gn->get_pos(); + if (pos.distance_to(mpos)<grab_r) { + + connecting=true; + connecting_from=gn->get_name(); + connecting_index=j; + connecting_out=true; + connecting_type=gn->get_connection_output_type(j); + connecting_color=gn->get_connection_output_color(j); + connecting_target=false; + connecting_to=pos; + return; + } + + + } + + for(int j=0;j<gn->get_connection_input_count();j++) { + + Vector2 pos = gn->get_connection_input_pos(j)+gn->get_pos(); + + if (pos.distance_to(mpos)<grab_r) { + + if (right_disconnects) { + //check disconnect + for (List<Connection>::Element*E=connections.front();E;E=E->next()) { + + if (E->get().to==gn->get_name() && E->get().to_port==j) { + + Node*fr = get_node(String(E->get().from)); + if (fr && fr->cast_to<GraphNode>()) { + + connecting_from=E->get().from; + connecting_index=E->get().from_port; + connecting_out=true; + connecting_type=fr->cast_to<GraphNode>()->get_connection_output_type(E->get().from_port); + connecting_color=fr->cast_to<GraphNode>()->get_connection_output_color(E->get().from_port); + connecting_target=false; + connecting_to=pos; + + emit_signal("disconnection_request",E->get().from,E->get().from_port,E->get().to,E->get().to_port); + fr = get_node(String(connecting_from)); //maybe it was erased + if (fr && fr->cast_to<GraphNode>()) { + connecting=true; + } + return; + } + + } + } + } + + + connecting=true; + connecting_from=gn->get_name(); + connecting_index=j; + connecting_out=false; + connecting_type=gn->get_connection_input_type(j); + connecting_color=gn->get_connection_input_color(j); + connecting_target=false; + connecting_to=pos; + return; + } + + + } + } + } + + if (p_ev.type==InputEvent::MOUSE_MOTION && connecting) { + + connecting_to=Vector2(p_ev.mouse_motion.x,p_ev.mouse_motion.y); + connecting_target=false; + top_layer->update(); + + Ref<Texture> port =get_icon("port","GraphNode"); + Vector2 mpos(p_ev.mouse_button.x,p_ev.mouse_button.y); + float grab_r=port->get_width()*0.5; + for(int i=get_child_count()-1;i>=0;i--) { + + GraphNode *gn=get_child(i)->cast_to<GraphNode>(); + if (!gn) + continue; + + if (!connecting_out) { + for(int j=0;j<gn->get_connection_output_count();j++) { + + Vector2 pos = gn->get_connection_output_pos(j)+gn->get_pos(); + int type =gn->get_connection_output_type(j); + if (type==connecting_type && pos.distance_to(mpos)<grab_r) { + + connecting_target=true; + connecting_to=pos; + connecting_target_to=gn->get_name(); + connecting_target_index=j; + return; + } + + + } + } else { + + for(int j=0;j<gn->get_connection_input_count();j++) { + + Vector2 pos = gn->get_connection_input_pos(j)+gn->get_pos(); + int type =gn->get_connection_input_type(j); + if (type==connecting_type && pos.distance_to(mpos)<grab_r) { + connecting_target=true; + connecting_to=pos; + connecting_target_to=gn->get_name(); + connecting_target_index=j; + return; + } + } + } + } + } + + if (p_ev.type==InputEvent::MOUSE_BUTTON && p_ev.mouse_button.button_index==BUTTON_LEFT && !p_ev.mouse_button.pressed) { + + if (connecting && connecting_target) { + + String from = connecting_from; + int from_slot = connecting_index; + String to =connecting_target_to; + int to_slot = connecting_target_index; + + if (!connecting_out) { + SWAP(from,to); + SWAP(from_slot,to_slot); + } + emit_signal("connection_request",from,from_slot,to,to_slot); + + } + connecting=false; + top_layer->update(); + + } + + + +} + +void GraphEdit::_draw_cos_line(const Vector2& p_from, const Vector2& p_to,const Color& p_color) { + + static const int steps = 20; + + Rect2 r; + r.pos=p_from; + r.expand_to(p_to); + Vector2 sign=Vector2((p_from.x < p_to.x) ? 1 : -1,(p_from.y < p_to.y) ? 1 : -1); + bool flip = sign.x * sign.y < 0; + + Vector2 prev; + for(int i=0;i<=steps;i++) { + + float d = i/float(steps); + float c=-Math::cos(d*Math_PI) * 0.5+0.5; + if (flip) + c=1.0-c; + Vector2 p = r.pos+Vector2(d*r.size.width,c*r.size.height); + + if (i>0) { + + top_layer->draw_line(prev,p,p_color,2); + } + + prev=p; + } +} + +void GraphEdit::_top_layer_draw() { + + _update_scroll(); + + if (connecting) { + + Node *fromn = get_node(connecting_from); + ERR_FAIL_COND(!fromn); + GraphNode *from = fromn->cast_to<GraphNode>(); + ERR_FAIL_COND(!from); + Vector2 pos; + if (connecting_out) + pos=from->get_connection_output_pos(connecting_index); + else + pos=from->get_connection_input_pos(connecting_index); + pos+=from->get_pos(); + + Vector2 topos; + topos=connecting_to; + + Color col=connecting_color; + + if (connecting_target) { + col.r+=0.4; + col.g+=0.4; + col.b+=0.4; + } + _draw_cos_line(pos,topos,col); + } + + List<List<Connection>::Element* > to_erase; + for(List<Connection>::Element *E=connections.front();E;E=E->next()) { + + NodePath fromnp(E->get().from); + + Node * from = get_node(fromnp); + if (!from) { + to_erase.push_back(E); + continue; + } + + GraphNode *gfrom = from->cast_to<GraphNode>(); + + if (!gfrom) { + to_erase.push_back(E); + continue; + } + + NodePath tonp(E->get().to); + Node * to = get_node(tonp); + if (!to) { + to_erase.push_back(E); + continue; + } + + GraphNode *gto = to->cast_to<GraphNode>(); + + if (!gto) { + to_erase.push_back(E); + continue; + } + + Vector2 frompos=gfrom->get_connection_output_pos(E->get().from_port)+gfrom->get_pos(); + Color color = gfrom->get_connection_output_color(E->get().from_port); + Vector2 topos=gto->get_connection_input_pos(E->get().to_port)+gto->get_pos(); + _draw_cos_line(frompos,topos,color); + + } + + while(to_erase.size()) { + connections.erase(to_erase.front()->get()); + to_erase.pop_front(); + } + if (box_selecting) + top_layer->draw_rect(box_selecting_rect,Color(0.7,0.7,1.0,0.3)); +} + +void GraphEdit::_input_event(const InputEvent& p_ev) { + + if (p_ev.type==InputEvent::MOUSE_MOTION && (p_ev.mouse_motion.button_mask&BUTTON_MASK_MIDDLE || (p_ev.mouse_motion.button_mask&BUTTON_MASK_LEFT && Input::get_singleton()->is_key_pressed(KEY_SPACE)))) { + h_scroll->set_val( h_scroll->get_val() - p_ev.mouse_motion.relative_x ); + v_scroll->set_val( v_scroll->get_val() - p_ev.mouse_motion.relative_y ); + } + + if (p_ev.type==InputEvent::MOUSE_MOTION && dragging) { + + just_selected=true; + drag_accum+=Vector2(p_ev.mouse_motion.relative_x,p_ev.mouse_motion.relative_y); + for(int i=get_child_count()-1;i>=0;i--) { + GraphNode *gn=get_child(i)->cast_to<GraphNode>(); + if (gn && gn->is_selected()) + gn->set_offset(gn->get_drag_from()+drag_accum); + } + } + + if (p_ev.type==InputEvent::MOUSE_MOTION && box_selecting) { + box_selecting_to = get_local_mouse_pos(); + + box_selecting_rect = Rect2(MIN(box_selecting_from.x,box_selecting_to.x), + MIN(box_selecting_from.y,box_selecting_to.y), + ABS(box_selecting_from.x-box_selecting_to.x), + ABS(box_selecting_from.y-box_selecting_to.y)); + + for(int i=get_child_count()-1;i>=0;i--) { + + GraphNode *gn=get_child(i)->cast_to<GraphNode>(); + if (!gn) + continue; + + bool in_box = gn->get_rect().intersects(box_selecting_rect); + + if (in_box) + gn->set_selected(box_selection_mode_aditive); + else + gn->set_selected(previus_selected.find(gn)!=NULL); + } + + top_layer->update(); + } + + if (p_ev.type==InputEvent::MOUSE_BUTTON) { + + const InputEventMouseButton &b=p_ev.mouse_button; + + if (b.button_index==BUTTON_RIGHT && b.pressed) + { + if (box_selecting) { + box_selecting = false; + for(int i=get_child_count()-1;i>=0;i--) { + + GraphNode *gn=get_child(i)->cast_to<GraphNode>(); + if (!gn) + continue; + + gn->set_selected(previus_selected.find(gn)!=NULL); + } + top_layer->update(); + } else { + emit_signal("popup_request", Vector2(b.global_x, b.global_y)); + } + } + + if (b.button_index==BUTTON_LEFT && !b.pressed && dragging) { + if (!just_selected && drag_accum==Vector2() && Input::get_singleton()->is_key_pressed(KEY_CONTROL)) { + //deselect current node + for(int i=get_child_count()-1;i>=0;i--) { + GraphNode *gn=get_child(i)->cast_to<GraphNode>(); + + if (gn && gn->get_rect().has_point(get_local_mouse_pos())) + gn->set_selected(false); + } + } + + if (drag_accum!=Vector2()) { + + emit_signal("_begin_node_move"); + + for(int i=get_child_count()-1;i>=0;i--) { + GraphNode *gn=get_child(i)->cast_to<GraphNode>(); + if (gn && gn->is_selected()) + gn->set_drag(false); + } + + emit_signal("_end_node_move"); + } + + dragging = false; + + top_layer->update(); + } + + if (b.button_index==BUTTON_LEFT && b.pressed) { + + GraphNode *gn; + for(int i=get_child_count()-1;i>=0;i--) { + + gn=get_child(i)->cast_to<GraphNode>(); + + if (gn && gn->get_rect().has_point(get_local_mouse_pos())) + break; + } + + if (gn) { + + if (_filter_input(Vector2(b.x,b.y))) + return; + + dragging = true; + drag_accum = Vector2(); + just_selected = !gn->is_selected(); + if(!gn->is_selected() && !Input::get_singleton()->is_key_pressed(KEY_CONTROL)) { + for (int i = 0; i < get_child_count(); i++) { + GraphNode *o_gn = get_child(i)->cast_to<GraphNode>(); + if (o_gn) + o_gn->set_selected(o_gn == gn); + } + } + + gn->set_selected(true); + for (int i = 0; i < get_child_count(); i++) { + GraphNode *o_gn = get_child(i)->cast_to<GraphNode>(); + if (!o_gn) + continue; + if (o_gn->is_selected()) + o_gn->set_drag(true); + } + + } else { + box_selecting = true; + box_selecting_from = get_local_mouse_pos(); + if (b.mod.control) { + box_selection_mode_aditive = true; + previus_selected.clear(); + for(int i=get_child_count()-1;i>=0;i--) { + + GraphNode *gn=get_child(i)->cast_to<GraphNode>(); + if (!gn || !gn->is_selected()) + continue; + + previus_selected.push_back(gn); + } + } else if (b.mod.shift) { + box_selection_mode_aditive = false; + previus_selected.clear(); + for(int i=get_child_count()-1;i>=0;i--) { + + GraphNode *gn=get_child(i)->cast_to<GraphNode>(); + if (!gn || !gn->is_selected()) + continue; + + previus_selected.push_back(gn); + } + } else { + box_selection_mode_aditive = true; + previus_selected.clear(); + for(int i=get_child_count()-1;i>=0;i--) { + + GraphNode *gn=get_child(i)->cast_to<GraphNode>(); + if (!gn) + continue; + + gn->set_selected(false); + } + } + } + } + + if (b.button_index==BUTTON_LEFT && !b.pressed && box_selecting) { + box_selecting = false; + previus_selected.clear(); + top_layer->update(); + } + } + + if (p_ev.type==InputEvent::KEY && p_ev.key.scancode==KEY_D && p_ev.key.pressed && p_ev.key.mod.command) { + emit_signal("duplicate_nodes_request"); + accept_event(); + } + + if (p_ev.type==InputEvent::KEY && p_ev.key.scancode==KEY_DELETE && p_ev.key.pressed) { + emit_signal("delete_nodes_request"); + accept_event(); + } + +} + +void GraphEdit::clear_connections() { + + connections.clear(); + update(); +} + + +void GraphEdit::set_right_disconnects(bool p_enable) { + + right_disconnects=p_enable; +} + +bool GraphEdit::is_right_disconnects_enabled() const{ + + return right_disconnects; +} + +Array GraphEdit::_get_connection_list() const { + + List<Connection> conns; + get_connection_list(&conns); + Array arr; + for(List<Connection>::Element *E=conns.front();E;E=E->next()) { + Dictionary d; + d["from"]=E->get().from; + d["from_port"]=E->get().from_port; + d["to"]=E->get().to; + d["to_port"]=E->get().to_port; + arr.push_back(d); + } + return arr; +} +void GraphEdit::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("connect_node:Error","from","from_port","to","to_port"),&GraphEdit::connect_node); + ObjectTypeDB::bind_method(_MD("is_node_connected","from","from_port","to","to_port"),&GraphEdit::is_node_connected); + ObjectTypeDB::bind_method(_MD("disconnect_node","from","from_port","to","to_port"),&GraphEdit::disconnect_node); + ObjectTypeDB::bind_method(_MD("get_connection_list"),&GraphEdit::_get_connection_list); + + ObjectTypeDB::bind_method(_MD("set_right_disconnects","enable"),&GraphEdit::set_right_disconnects); + ObjectTypeDB::bind_method(_MD("is_right_disconnects_enabled"),&GraphEdit::is_right_disconnects_enabled); + + ObjectTypeDB::bind_method(_MD("_graph_node_moved"),&GraphEdit::_graph_node_moved); + ObjectTypeDB::bind_method(_MD("_graph_node_raised"),&GraphEdit::_graph_node_raised); + + ObjectTypeDB::bind_method(_MD("_top_layer_input"),&GraphEdit::_top_layer_input); + ObjectTypeDB::bind_method(_MD("_top_layer_draw"),&GraphEdit::_top_layer_draw); + ObjectTypeDB::bind_method(_MD("_scroll_moved"),&GraphEdit::_scroll_moved); + + ObjectTypeDB::bind_method(_MD("_input_event"),&GraphEdit::_input_event); + + ADD_SIGNAL(MethodInfo("connection_request",PropertyInfo(Variant::STRING,"from"),PropertyInfo(Variant::INT,"from_slot"),PropertyInfo(Variant::STRING,"to"),PropertyInfo(Variant::INT,"to_slot"))); + ADD_SIGNAL(MethodInfo("disconnection_request",PropertyInfo(Variant::STRING,"from"),PropertyInfo(Variant::INT,"from_slot"),PropertyInfo(Variant::STRING,"to"),PropertyInfo(Variant::INT,"to_slot"))); + ADD_SIGNAL(MethodInfo("popup_request", PropertyInfo(Variant::VECTOR2,"p_position"))); + ADD_SIGNAL(MethodInfo("duplicate_nodes_request")); + ADD_SIGNAL(MethodInfo("delete_nodes_request")); + ADD_SIGNAL(MethodInfo("_begin_node_move")); + ADD_SIGNAL(MethodInfo("_end_node_move")); +} + + + +GraphEdit::GraphEdit() { + set_focus_mode(FOCUS_ALL); + + top_layer=NULL; + top_layer=memnew(GraphEditFilter(this)); + add_child(top_layer); + top_layer->set_stop_mouse(false); + top_layer->set_area_as_parent_rect(); + top_layer->connect("draw",this,"_top_layer_draw"); + top_layer->set_stop_mouse(false); + top_layer->connect("input_event",this,"_top_layer_input"); + + h_scroll = memnew(HScrollBar); + h_scroll->set_name("_h_scroll"); + top_layer->add_child(h_scroll); + + v_scroll = memnew(VScrollBar); + v_scroll->set_name("_v_scroll"); + top_layer->add_child(v_scroll); + updating=false; + connecting=false; + right_disconnects=false; + + box_selecting = false; + dragging = false; + + h_scroll->connect("value_changed", this,"_scroll_moved"); + v_scroll->connect("value_changed", this,"_scroll_moved"); +} diff --git a/scene/gui/graph_edit.h b/scene/gui/graph_edit.h new file mode 100644 index 0000000000..44f5a369c2 --- /dev/null +++ b/scene/gui/graph_edit.h @@ -0,0 +1,109 @@ +#ifndef GRAPH_EDIT_H +#define GRAPH_EDIT_H + +#include "scene/gui/graph_node.h" +#include "scene/gui/scroll_bar.h" + +class GraphEdit; + +class GraphEditFilter : public Control { + + OBJ_TYPE(GraphEditFilter,Control); + + friend class GraphEdit; + GraphEdit *ge; + virtual bool has_point(const Point2& p_point) const; + +public: + + + GraphEditFilter(GraphEdit *p_edit); +}; + +class GraphEdit : public Control { + + OBJ_TYPE(GraphEdit,Control); +public: + + struct Connection { + StringName from; + StringName to; + int from_port; + int to_port; + + }; +private: + + HScrollBar* h_scroll; + VScrollBar* v_scroll; + + + bool connecting; + String connecting_from; + bool connecting_out; + int connecting_index; + int connecting_type; + Color connecting_color; + bool connecting_target; + Vector2 connecting_to; + String connecting_target_to; + int connecting_target_index; + + bool dragging; + bool just_selected; + Vector2 drag_accum; + + bool box_selecting; + bool box_selection_mode_aditive; + Point2 box_selecting_from; + Point2 box_selecting_to; + Rect2 box_selecting_rect; + List<GraphNode*> previus_selected; + + bool right_disconnects; + bool updating; + List<Connection> connections; + + void _draw_cos_line(const Vector2& p_from, const Vector2& p_to,const Color& p_color); + + void _graph_node_raised(Node* p_gn); + void _graph_node_moved(Node *p_gn); + + void _update_scroll(); + void _scroll_moved(double); + void _input_event(const InputEvent& p_ev); + + GraphEditFilter *top_layer; + void _top_layer_input(const InputEvent& p_ev); + void _top_layer_draw(); + void _update_scroll_offset(); + + Array _get_connection_list() const; + + friend class GraphEditFilter; + bool _filter_input(const Point2& p_point); +protected: + + static void _bind_methods(); + virtual void add_child_notify(Node *p_child); + virtual void remove_child_notify(Node *p_child); + void _notification(int p_what); + +public: + + Error connect_node(const StringName& p_from, int p_from_port,const StringName& p_to,int p_to_port); + bool is_node_connected(const StringName& p_from, int p_from_port,const StringName& p_to,int p_to_port); + void disconnect_node(const StringName& p_from, int p_from_port,const StringName& p_to,int p_to_port); + void clear_connections(); + + GraphEditFilter *get_top_layer() const { return top_layer; } + void get_connection_list(List<Connection> *r_connections) const; + + void set_right_disconnects(bool p_enable); + bool is_right_disconnects_enabled() const; + + + GraphEdit(); +}; + +#endif // GRAPHEdit_H diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp new file mode 100644 index 0000000000..5efc9757b7 --- /dev/null +++ b/scene/gui/graph_node.cpp @@ -0,0 +1,589 @@ +#include "graph_node.h" +#include "method_bind_ext.inc" + + +bool GraphNode::_set(const StringName& p_name, const Variant& p_value) { + + if (!p_name.operator String().begins_with("slot/")) + return false; + + int idx=p_name.operator String().get_slice("/",1).to_int(); + String what = p_name.operator String().get_slice("/",2); + + + Slot si; + if (slot_info.has(idx)) + si=slot_info[idx]; + + + if (what=="left_enabled") + si.enable_left=p_value; + else if (what=="left_type") + si.type_left=p_value; + else if (what=="left_color") + si.color_left=p_value; + else if (what=="right_enabled") + si.enable_right=p_value; + else if (what=="right_type") + si.type_right=p_value; + else if (what=="right_color") + si.color_right=p_value; + else + return false; + + set_slot(idx,si.enable_left,si.type_left,si.color_left,si.enable_right,si.type_right,si.color_right); + update(); + return true; +} + +bool GraphNode::_get(const StringName& p_name,Variant &r_ret) const{ + + + + if (!p_name.operator String().begins_with("slot/")) { + return false; + } + + int idx=p_name.operator String().get_slice("/",1).to_int(); + String what = p_name.operator String().get_slice("/",2); + + + + Slot si; + if (slot_info.has(idx)) + si=slot_info[idx]; + + if (what=="left_enabled") + r_ret=si.enable_left; + else if (what=="left_type") + r_ret=si.type_left; + else if (what=="left_color") + r_ret=si.color_left; + else if (what=="right_enabled") + r_ret=si.enable_right; + else if (what=="right_type") + r_ret=si.type_right; + else if (what=="right_color") + r_ret=si.color_right; + else + return false; + + return true; +} +void GraphNode::_get_property_list( List<PropertyInfo> *p_list) const{ + + int idx=0; + for(int i=0;i<get_child_count();i++) { + Control *c=get_child(i)->cast_to<Control>(); + if (!c || c->is_set_as_toplevel() ) + continue; + + String base="slot/"+itos(idx)+"/"; + + p_list->push_back(PropertyInfo(Variant::BOOL,base+"left_enabled")); + p_list->push_back(PropertyInfo(Variant::INT,base+"left_type")); + p_list->push_back(PropertyInfo(Variant::COLOR,base+"left_color")); + p_list->push_back(PropertyInfo(Variant::BOOL,base+"right_enabled")); + p_list->push_back(PropertyInfo(Variant::INT,base+"right_type")); + p_list->push_back(PropertyInfo(Variant::COLOR,base+"right_color")); + + idx++; + } +} + + +void GraphNode::_resort() { + + + + int sep=get_constant("separation"); + Ref<StyleBox> sb=get_stylebox("frame"); + bool first=true; + + Size2 minsize; + + for(int i=0;i<get_child_count();i++) { + Control *c=get_child(i)->cast_to<Control>(); + if (!c) + continue; + if (c->is_set_as_toplevel()) + continue; + + Size2i size=c->get_combined_minimum_size(); + + minsize.y+=size.y; + minsize.x=MAX(minsize.x,size.x); + + if (first) + first=false; + else + minsize.y+=sep; + + } + + + int vofs=0; + int w = get_size().x - sb->get_minimum_size().x; + + + cache_y.clear(); + for(int i=0;i<get_child_count();i++) { + Control *c=get_child(i)->cast_to<Control>(); + if (!c) + continue; + if (c->is_set_as_toplevel()) + continue; + + Size2i size=c->get_combined_minimum_size(); + + Rect2 r(sb->get_margin(MARGIN_LEFT),sb->get_margin(MARGIN_TOP)+vofs,w,size.y); + + fit_child_in_rect(c,r); + cache_y.push_back(vofs+size.y*0.5); + + if (vofs>0) + vofs+=sep; + vofs+=size.y; + + + } + + _change_notify(); + update(); + connpos_dirty=true; + + +} + + +void GraphNode::_notification(int p_what) { + + if (p_what==NOTIFICATION_DRAW) { + + Ref<StyleBox> sb=get_stylebox(selected ? "selectedframe" : "frame"); + Ref<Texture> port =get_icon("port"); + Ref<Texture> close =get_icon("close"); + int close_offset = get_constant("close_offset"); + Ref<Font> title_font = get_font("title_font"); + int title_offset = get_constant("title_offset"); + Color title_color = get_color("title_color"); + Point2i icofs = -port->get_size()*0.5; + int edgeofs=get_constant("port_offset"); + icofs.y+=sb->get_margin(MARGIN_TOP); + draw_style_box(sb,Rect2(Point2(),get_size())); + + int w = get_size().width-sb->get_minimum_size().x; + + if (show_close) + w-=close->get_width(); + + draw_string(title_font,Point2(sb->get_margin(MARGIN_LEFT),-title_font->get_height()+title_font->get_ascent()+title_offset),title,title_color,w); + if (show_close) { + Vector2 cpos = Point2(w+sb->get_margin(MARGIN_LEFT),-close->get_height()+close_offset); + draw_texture(close,cpos); + close_rect.pos=cpos; + close_rect.size=close->get_size(); + } else { + close_rect=Rect2(); + } + + for (Map<int,Slot>::Element *E=slot_info.front();E;E=E->next()) { + + if (E->key() < 0 || E->key()>=cache_y.size()) + continue; + if (!slot_info.has(E->key())) + continue; + const Slot &s=slot_info[E->key()]; + //left + if (s.enable_left) + port->draw(get_canvas_item(),icofs+Point2(edgeofs,cache_y[E->key()]),s.color_left); + if (s.enable_right) + port->draw(get_canvas_item(),icofs+Point2(get_size().x-edgeofs,cache_y[E->key()]),s.color_right); + + } + } + + if (p_what==NOTIFICATION_SORT_CHILDREN) { + + _resort(); + } + +} + + +void GraphNode::set_slot(int p_idx,bool p_enable_left,int p_type_left,const Color& p_color_left, bool p_enable_right,int p_type_right,const Color& p_color_right) { + + ERR_FAIL_COND(p_idx<0); + + if (!p_enable_left && p_type_left==0 && p_color_left==Color(1,1,1,1) && !p_enable_right && p_type_right==0 && p_color_right==Color(1,1,1,1)) { + slot_info.erase(p_idx); + return; + } + + Slot s; + s.enable_left=p_enable_left; + s.type_left=p_type_left; + s.color_left=p_color_left; + s.enable_right=p_enable_right; + s.type_right=p_type_right; + s.color_right=p_color_right; + slot_info[p_idx]=s; + update(); + connpos_dirty=true; + +} + +void GraphNode::clear_slot(int p_idx){ + + slot_info.erase(p_idx); + update(); + connpos_dirty=true; + +} +void GraphNode::clear_all_slots(){ + + slot_info.clear(); + update(); + connpos_dirty=true; + +} +bool GraphNode::is_slot_enabled_left(int p_idx) const{ + + if (!slot_info.has(p_idx)) + return false; + return slot_info[p_idx].enable_left; + +} + +int GraphNode::get_slot_type_left(int p_idx) const{ + + if (!slot_info.has(p_idx)) + return 0; + return slot_info[p_idx].type_left; + +} + +Color GraphNode::get_slot_color_left(int p_idx) const{ + + if (!slot_info.has(p_idx)) + return Color(1,1,1,1); + return slot_info[p_idx].color_left; + +} + +bool GraphNode::is_slot_enabled_right(int p_idx) const{ + + if (!slot_info.has(p_idx)) + return false; + return slot_info[p_idx].enable_right; + +} + + + +int GraphNode::get_slot_type_right(int p_idx) const{ + + if (!slot_info.has(p_idx)) + return 0; + return slot_info[p_idx].type_right; + +} + +Color GraphNode::get_slot_color_right(int p_idx) const{ + + if (!slot_info.has(p_idx)) + return Color(1,1,1,1); + return slot_info[p_idx].color_right; + +} + +Size2 GraphNode::get_minimum_size() const { + + Ref<Font> title_font = get_font("title_font"); + + int sep=get_constant("separation"); + Ref<StyleBox> sb=get_stylebox("frame"); + bool first=true; + + Size2 minsize; + minsize.x=title_font->get_string_size(title).x; + if (show_close) { + Ref<Texture> close =get_icon("close"); + minsize.x+=sep+close->get_width(); + } + + + for(int i=0;i<get_child_count();i++) { + + Control *c=get_child(i)->cast_to<Control>(); + if (!c) + continue; + if (c->is_set_as_toplevel()) + continue; + + Size2i size=c->get_combined_minimum_size(); + + minsize.y+=size.y; + minsize.x=MAX(minsize.x,size.x); + + if (first) + first=false; + else + minsize.y+=sep; + } + + return minsize+sb->get_minimum_size(); +} + +void GraphNode::set_title(const String& p_title) { + + title=p_title; + minimum_size_changed(); + update(); + +} + +String GraphNode::get_title() const{ + + return title; +} + +void GraphNode::set_offset(const Vector2& p_offset) { + + offset=p_offset; + emit_signal("offset_changed"); + update(); +} + +Vector2 GraphNode::get_offset() const { + + return offset; +} + +void GraphNode::set_selected(bool p_selected) +{ + selected = p_selected; + update(); +} + +bool GraphNode::is_selected() +{ + return selected; +} + +void GraphNode::set_drag(bool p_drag) +{ + if (p_drag) + drag_from=get_offset(); + else + emit_signal("dragged",drag_from,get_offset()); //useful for undo/redo +} + +Vector2 GraphNode::get_drag_from() +{ + return drag_from; +} + + +void GraphNode::set_show_close_button(bool p_enable){ + + show_close=p_enable; + update(); +} +bool GraphNode::is_close_button_visible() const{ + + return show_close; +} + +void GraphNode::_connpos_update() { + + + int edgeofs=get_constant("port_offset"); + int sep=get_constant("separation"); + + Ref<StyleBox> sb=get_stylebox("frame"); + conn_input_cache.clear(); + conn_output_cache.clear(); + int vofs=0; + + int idx=0; + + for(int i=0;i<get_child_count();i++) { + Control *c=get_child(i)->cast_to<Control>(); + if (!c) + continue; + if (c->is_set_as_toplevel()) + continue; + + Size2i size=c->get_combined_minimum_size(); + + int y = sb->get_margin(MARGIN_TOP)+vofs; + int h = size.y; + + + if (slot_info.has(idx)) { + + if (slot_info[idx].enable_left) { + ConnCache cc; + cc.pos=Point2i(edgeofs,y+h/2); + cc.type=slot_info[idx].type_left; + cc.color=slot_info[idx].color_left; + conn_input_cache.push_back(cc); + } + if (slot_info[idx].enable_right) { + ConnCache cc; + cc.pos=Point2i(get_size().width-edgeofs,y+h/2); + cc.type=slot_info[idx].type_right; + cc.color=slot_info[idx].color_right; + conn_output_cache.push_back(cc); + } + } + + if (vofs>0) + vofs+=sep; + vofs+=size.y; + idx++; + + } + + + connpos_dirty=false; +} + +int GraphNode::get_connection_input_count() { + + if (connpos_dirty) + _connpos_update(); + + return conn_input_cache.size(); + +} +int GraphNode::get_connection_output_count() { + + if (connpos_dirty) + _connpos_update(); + + return conn_output_cache.size(); + +} + + +Vector2 GraphNode::get_connection_input_pos(int p_idx) { + + if (connpos_dirty) + _connpos_update(); + + ERR_FAIL_INDEX_V(p_idx,conn_input_cache.size(),Vector2()); + return conn_input_cache[p_idx].pos; +} + +int GraphNode::get_connection_input_type(int p_idx) { + + if (connpos_dirty) + _connpos_update(); + + ERR_FAIL_INDEX_V(p_idx,conn_input_cache.size(),0); + return conn_input_cache[p_idx].type; +} + +Color GraphNode::get_connection_input_color(int p_idx) { + + if (connpos_dirty) + _connpos_update(); + + ERR_FAIL_INDEX_V(p_idx,conn_input_cache.size(),Color()); + return conn_input_cache[p_idx].color; +} + +Vector2 GraphNode::get_connection_output_pos(int p_idx){ + + if (connpos_dirty) + _connpos_update(); + + ERR_FAIL_INDEX_V(p_idx,conn_output_cache.size(),Vector2()); + return conn_output_cache[p_idx].pos; + +} + +int GraphNode::get_connection_output_type(int p_idx) { + + if (connpos_dirty) + _connpos_update(); + + ERR_FAIL_INDEX_V(p_idx,conn_output_cache.size(),0); + return conn_output_cache[p_idx].type; +} + +Color GraphNode::get_connection_output_color(int p_idx) { + + if (connpos_dirty) + _connpos_update(); + + ERR_FAIL_INDEX_V(p_idx,conn_output_cache.size(),Color()); + return conn_output_cache[p_idx].color; +} + +void GraphNode::_input_event(const InputEvent& p_ev) { + + if (p_ev.type==InputEvent::MOUSE_BUTTON) { + get_parent_control()->grab_focus(); + if(p_ev.mouse_button.pressed && p_ev.mouse_button.button_index==BUTTON_LEFT) { + + Vector2 mpos = Vector2(p_ev.mouse_button.x,p_ev.mouse_button.y); + if (close_rect.size!=Size2() && close_rect.has_point(mpos)) { + emit_signal("close_request"); + return; + } + emit_signal("raise_request"); + } + } + +} + + +void GraphNode::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("set_title","title"),&GraphNode::set_title); + ObjectTypeDB::bind_method(_MD("get_title"),&GraphNode::get_title); + ObjectTypeDB::bind_method(_MD("_input_event"),&GraphNode::_input_event); + + ObjectTypeDB::bind_method(_MD("set_slot","idx","enable_left","type_left","color_left","enable_right","type_right","color_right"),&GraphNode::set_slot); + ObjectTypeDB::bind_method(_MD("clear_slot","idx"),&GraphNode::clear_slot); + ObjectTypeDB::bind_method(_MD("clear_all_slots","idx"),&GraphNode::clear_all_slots); + ObjectTypeDB::bind_method(_MD("is_slot_enabled_left","idx"),&GraphNode::is_slot_enabled_left); + ObjectTypeDB::bind_method(_MD("get_slot_type_left","idx"),&GraphNode::get_slot_type_left); + ObjectTypeDB::bind_method(_MD("get_slot_color_left","idx"),&GraphNode::get_slot_color_left); + ObjectTypeDB::bind_method(_MD("is_slot_enabled_right","idx"),&GraphNode::is_slot_enabled_right); + ObjectTypeDB::bind_method(_MD("get_slot_type_right","idx"),&GraphNode::get_slot_type_right); + ObjectTypeDB::bind_method(_MD("get_slot_color_right","idx"),&GraphNode::get_slot_color_right); + + ObjectTypeDB::bind_method(_MD("set_offset","offset"),&GraphNode::set_offset); + ObjectTypeDB::bind_method(_MD("get_offset"),&GraphNode::get_offset); + + ObjectTypeDB::bind_method(_MD("get_connection_output_count"),&GraphNode::get_connection_output_count); + ObjectTypeDB::bind_method(_MD("get_connection_input_count"),&GraphNode::get_connection_input_count); + + ObjectTypeDB::bind_method(_MD("get_connection_output_pos","idx"),&GraphNode::get_connection_output_pos); + ObjectTypeDB::bind_method(_MD("get_connection_output_type","idx"),&GraphNode::get_connection_output_type); + ObjectTypeDB::bind_method(_MD("get_connection_output_color","idx"),&GraphNode::get_connection_output_color); + ObjectTypeDB::bind_method(_MD("get_connection_input_pos","idx"),&GraphNode::get_connection_input_pos); + ObjectTypeDB::bind_method(_MD("get_connection_input_type","idx"),&GraphNode::get_connection_input_type); + ObjectTypeDB::bind_method(_MD("get_connection_input_color","idx"),&GraphNode::get_connection_input_color); + + + ObjectTypeDB::bind_method(_MD("set_show_close_button","show"),&GraphNode::set_show_close_button); + ObjectTypeDB::bind_method(_MD("is_close_button_visible"),&GraphNode::is_close_button_visible); + + ADD_PROPERTY( PropertyInfo(Variant::STRING,"title"),_SCS("set_title"),_SCS("get_title")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"show_close"),_SCS("set_show_close_button"),_SCS("is_close_button_visible")); + + ADD_SIGNAL(MethodInfo("offset_changed")); + ADD_SIGNAL(MethodInfo("dragged",PropertyInfo(Variant::VECTOR2,"from"),PropertyInfo(Variant::VECTOR2,"to"))); + ADD_SIGNAL(MethodInfo("raise_request")); + ADD_SIGNAL(MethodInfo("close_request")); +} + +GraphNode::GraphNode() { + show_close=false; + connpos_dirty=true; +} diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h new file mode 100644 index 0000000000..201529380d --- /dev/null +++ b/scene/gui/graph_node.h @@ -0,0 +1,106 @@ +#ifndef GRAPH_NODE_H +#define GRAPH_NODE_H + +#include "scene/gui/container.h" + +class GraphNode : public Container { + + OBJ_TYPE(GraphNode,Container); + + + + struct Slot { + bool enable_left; + int type_left; + Color color_left; + bool enable_right; + int type_right; + Color color_right; + + + Slot() { enable_left=false; type_left=0; color_left=Color(1,1,1,1); enable_right=false; type_right=0; color_right=Color(1,1,1,1); } + }; + + String title; + bool show_close; + Vector2 offset; + + Rect2 close_rect; + + Vector<int> cache_y; + + struct ConnCache { + Vector2 pos; + int type; + Color color; + }; + + Vector<ConnCache> conn_input_cache; + Vector<ConnCache> conn_output_cache; + + Map<int,Slot> slot_info; + + bool connpos_dirty; + + void _connpos_update(); + void _resort(); + + Vector2 drag_from; + bool selected; +protected: + + void _input_event(const InputEvent& p_ev); + void _notification(int p_what); + static void _bind_methods(); + + bool _set(const StringName& p_name, const Variant& p_value); + bool _get(const StringName& p_name,Variant &r_ret) const; + void _get_property_list( List<PropertyInfo> *p_list) const; + +public: + + + + + void set_slot(int p_idx,bool p_enable_left,int p_type_left,const Color& p_color_left, bool p_enable_right,int p_type_right,const Color& p_color_right); + void clear_slot(int p_idx); + void clear_all_slots(); + bool is_slot_enabled_left(int p_idx) const; + int get_slot_type_left(int p_idx) const; + Color get_slot_color_left(int p_idx) const; + bool is_slot_enabled_right(int p_idx) const; + int get_slot_type_right(int p_idx) const; + Color get_slot_color_right(int p_idx) const; + + void set_title(const String& p_title); + String get_title() const; + + void set_offset(const Vector2& p_offset); + Vector2 get_offset() const; + + void set_selected(bool p_selected); + bool is_selected(); + + void set_drag(bool p_drag); + Vector2 get_drag_from(); + + void set_show_close_button(bool p_enable); + bool is_close_button_visible() const; + + int get_connection_input_count() ; + int get_connection_output_count() ; + Vector2 get_connection_input_pos(int p_idx); + int get_connection_input_type(int p_idx); + Color get_connection_input_color(int p_idx); + Vector2 get_connection_output_pos(int p_idx); + int get_connection_output_type(int p_idx); + Color get_connection_output_color(int p_idx); + + + virtual Size2 get_minimum_size() const; + + GraphNode(); +}; + + +#endif // GRAPH_NODE_H diff --git a/scene/gui/grid_container.cpp b/scene/gui/grid_container.cpp index f54345cdb8..105f66f368 100644 --- a/scene/gui/grid_container.cpp +++ b/scene/gui/grid_container.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -40,7 +40,8 @@ void GridContainer::_notification(int p_what) { Set<int> col_expanded; Set<int> row_expanded; - int sep=get_constant("separation"); + int hsep=get_constant("hseparation"); + int vsep=get_constant("vseparation"); int idx=0; int max_row=0; @@ -69,6 +70,7 @@ void GridContainer::_notification(int p_what) { else row_minh[row]=ms.height; + // print_line("store row "+itos(row)+" mw "+itos(ms.height)); if (c->get_h_size_flags()&SIZE_EXPAND) col_expanded.insert(col); @@ -96,8 +98,8 @@ void GridContainer::_notification(int p_what) { expand_rows++; } - ms.height+=sep*max_row; - ms.width+=sep*max_col; + ms.height+=vsep*max_row; + ms.width+=hsep*max_col; int row_expand = expand_rows?(size.y-ms.y)/expand_rows:0; int col_expand = expand_cols?(size.x-ms.x)/expand_cols:0; @@ -118,29 +120,28 @@ void GridContainer::_notification(int p_what) { if (col==0) { col_ofs=0; if (row>0 && row_minh.has(row-1)) - row_ofs+=row_minh[row-1]+sep+(row_expanded.has(row-1)?row_expand:0); + row_ofs+=row_minh[row-1]+vsep+(row_expanded.has(row-1)?row_expand:0); } - if (c->is_visible()) { - Size2 s; - if (col_minw.has(col)) - s.width=col_minw[col]; - if (row_minh.has(row)) - s.height=row_minh[col]; - - if (row_expanded.has(row)) - s.height+=row_expand; - if (col_expanded.has(col)) - s.width+=col_expand; + Size2 s; + if (col_minw.has(col)) + s.width=col_minw[col]; + if (row_minh.has(row)) + s.height=row_minh[row]; - Point2 p(col_ofs,row_ofs); + if (row_expanded.has(row)) + s.height+=row_expand; + if (col_expanded.has(col)) + s.width+=col_expand; - fit_child_in_rect(c,Rect2(p,s)); + Point2 p(col_ofs,row_ofs); - } +// print_line("col: "+itos(col)+" row: "+itos(row)+" col_ofs: "+itos(col_ofs)+" row_ofs: "+itos(row_ofs)); + fit_child_in_rect(c,Rect2(p,s)); + //print_line("col: "+itos(col)+" row: "+itos(row)+" rect: "+Rect2(p,s)); if (col_minw.has(col)) { - col_ofs+=col_minw[col]+sep+(col_expanded.has(col)?col_expand:0); + col_ofs+=col_minw[col]+hsep+(col_expanded.has(col)?col_expand:0); } idx++; @@ -178,7 +179,8 @@ Size2 GridContainer::get_minimum_size() const { Map<int,int> col_minw; Map<int,int> row_minh; - int sep=get_constant("separation"); + int hsep=get_constant("hseparation"); + int vsep=get_constant("vseparation"); int idx=0; int max_row=0; @@ -216,8 +218,8 @@ Size2 GridContainer::get_minimum_size() const { ms.height+=E->get(); } - ms.height+=sep*max_row; - ms.width+=sep*max_col; + ms.height+=vsep*max_row; + ms.width+=hsep*max_col; return ms; @@ -226,5 +228,6 @@ Size2 GridContainer::get_minimum_size() const { GridContainer::GridContainer() { + set_stop_mouse(false); columns=1; } diff --git a/scene/gui/grid_container.h b/scene/gui/grid_container.h index f74ad6c66a..8d8bc27293 100644 --- a/scene/gui/grid_container.h +++ b/scene/gui/grid_container.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/item_list.cpp b/scene/gui/item_list.cpp new file mode 100644 index 0000000000..f035cb7722 --- /dev/null +++ b/scene/gui/item_list.cpp @@ -0,0 +1,1148 @@ +#include "item_list.h" +#include "os/os.h" +#include "globals.h" + + +void ItemList::add_item(const String& p_item,const Ref<Texture>& p_texture,bool p_selectable) { + + Item item; + item.icon=p_texture; + item.text=p_item; + item.selectable=p_selectable; + item.selected=false; + item.disabled=false; + item.custom_bg=Color(0,0,0,0); + items.push_back(item); + + update(); + shape_changed=true; + +} + +void ItemList::add_icon_item(const Ref<Texture>& p_item,bool p_selectable){ + + Item item; + item.icon=p_item; + //item.text=p_item; + item.selectable=p_selectable; + item.selected=false; + item.disabled=false; + item.custom_bg=Color(0,0,0,0); + items.push_back(item); + + update(); + shape_changed=true; + +} + +void ItemList::set_item_text(int p_idx,const String& p_text){ + + ERR_FAIL_INDEX(p_idx,items.size()); + + items[p_idx].text=p_text; + update(); + shape_changed=true; + +} + +String ItemList::get_item_text(int p_idx) const{ + + ERR_FAIL_INDEX_V(p_idx,items.size(),String()); + return items[p_idx].text; + +} + +void ItemList::set_item_tooltip(int p_idx,const String& p_tooltip){ + + ERR_FAIL_INDEX(p_idx,items.size()); + + items[p_idx].tooltip=p_tooltip; + update(); + shape_changed=true; + +} + +String ItemList::get_item_tooltip(int p_idx) const{ + + ERR_FAIL_INDEX_V(p_idx,items.size(),String()); + return items[p_idx].tooltip; + +} + +void ItemList::set_item_icon(int p_idx,const Ref<Texture>& p_icon){ + + ERR_FAIL_INDEX(p_idx,items.size()); + + items[p_idx].icon=p_icon; + update(); + shape_changed=true; + + +} +Ref<Texture> ItemList::get_item_icon(int p_idx) const{ + + ERR_FAIL_INDEX_V(p_idx,items.size(),Ref<Texture>()); + + return items[p_idx].icon; + +} + +void ItemList::set_item_custom_bg_color(int p_idx,const Color& p_custom_bg_color) { + + ERR_FAIL_INDEX(p_idx,items.size()); + + items[p_idx].custom_bg=p_custom_bg_color; + +} + +Color ItemList::get_item_custom_bg_color(int p_idx) const { + + ERR_FAIL_INDEX_V(p_idx,items.size(),Color()); + + return items[p_idx].custom_bg; + +} + + + +void ItemList::set_item_tag_icon(int p_idx,const Ref<Texture>& p_tag_icon){ + + ERR_FAIL_INDEX(p_idx,items.size()); + + items[p_idx].tag_icon=p_tag_icon; + update(); + shape_changed=true; + + +} +Ref<Texture> ItemList::get_item_tag_icon(int p_idx) const{ + + ERR_FAIL_INDEX_V(p_idx,items.size(),Ref<Texture>()); + + return items[p_idx].tag_icon; + +} + +void ItemList::set_item_selectable(int p_idx,bool p_selectable){ + + ERR_FAIL_INDEX(p_idx,items.size()); + + items[p_idx].selectable=p_selectable; + + +} + + +bool ItemList::is_item_selectable(int p_idx) const{ + + ERR_FAIL_INDEX_V(p_idx,items.size(),false); + return items[p_idx].selectable; +} + +void ItemList::set_item_disabled(int p_idx,bool p_disabled){ + + ERR_FAIL_INDEX(p_idx,items.size()); + + items[p_idx].disabled=p_disabled; + + +} + + +bool ItemList::is_item_disabled(int p_idx) const{ + + ERR_FAIL_INDEX_V(p_idx,items.size(),false); + return items[p_idx].disabled; +} + + +void ItemList::set_item_metadata(int p_idx,const Variant& p_metadata){ + + ERR_FAIL_INDEX(p_idx,items.size()); + + items[p_idx].metadata=p_metadata; + update(); + shape_changed=true; + +} + +Variant ItemList::get_item_metadata(int p_idx) const{ + + ERR_FAIL_INDEX_V(p_idx,items.size(),Variant()); + return items[p_idx].metadata; + +} +void ItemList::select(int p_idx,bool p_single){ + + ERR_FAIL_INDEX(p_idx,items.size()); + + if (p_single || select_mode==SELECT_SINGLE) { + + if (!items[p_idx].selectable) { + return; + } + + for(int i=0;i<items.size();i++) { + items[i].selected=p_idx==i; + } + + current=p_idx; + ensure_selected_visible=false; + } else { + + if (items[p_idx].selectable) { + items[p_idx].selected=true; + } + } + update(); + + +} +void ItemList::unselect(int p_idx){ + + ERR_FAIL_INDEX(p_idx,items.size()); + + if (select_mode!=SELECT_MULTI) { + items[p_idx].selected=false; + current=-1; + } else { + items[p_idx].selected=false; + } + update(); + +} +bool ItemList::is_selected(int p_idx) const{ + + ERR_FAIL_INDEX_V(p_idx,items.size(),false); + + return items[p_idx].selected; + +} + +void ItemList::set_current(int p_current) { + ERR_FAIL_INDEX(p_current,items.size()); + + if (select_mode==SELECT_SINGLE) + select(p_current,true); + else { + current=p_current; + update(); + } +} + +int ItemList::get_current() const { + + return current; +} + +void ItemList::move_item(int p_item,int p_to_pos) { + + ERR_FAIL_INDEX(p_item,items.size()); + ERR_FAIL_INDEX(p_to_pos,items.size()+1); + + Item it=items[p_item]; + items.remove(p_item);; + + if (p_to_pos>p_item) { + p_to_pos--; + } + + if (p_to_pos>=items.size()) { + items.push_back(it); + } else { + items.insert(p_to_pos,it); + } + + if (current<0) { + //do none + } if (p_item==current) { + current=p_to_pos; + } else if (p_to_pos>p_item && current>p_item && current<p_to_pos) { + current--; + } else if (p_to_pos<p_item && current<p_item && current>p_to_pos) { + current++; + } + + + update(); +} + +int ItemList::get_item_count() const{ + + return items.size(); +} +void ItemList::remove_item(int p_idx){ + + ERR_FAIL_INDEX(p_idx,items.size()); + + items.remove(p_idx); + update(); + shape_changed=true; + + +} + +void ItemList::clear(){ + + items.clear(); + current=-1; + ensure_selected_visible=false; + update(); + +} + +void ItemList::set_fixed_column_width(int p_size){ + + ERR_FAIL_COND(p_size<0); + fixed_column_width=p_size; + update(); + shape_changed=true; + +} +int ItemList::get_fixed_column_width() const{ + + return fixed_column_width; +} + +void ItemList::set_max_text_lines(int p_lines){ + + ERR_FAIL_COND(p_lines<1); + max_text_lines=p_lines; + update(); + shape_changed=true; + +} +int ItemList::get_max_text_lines() const{ + + return max_text_lines; +} + +void ItemList::set_max_columns(int p_amount){ + + ERR_FAIL_COND(p_amount<0); + max_columns=p_amount; + update(); +} +int ItemList::get_max_columns() const{ + + return max_columns; +} + +void ItemList::set_select_mode(SelectMode p_mode) { + + select_mode=p_mode; + update(); +} + +ItemList::SelectMode ItemList::get_select_mode() const { + + return select_mode; +} + +void ItemList::set_icon_mode(IconMode p_mode){ + + icon_mode=p_mode; + update(); + shape_changed=true; + +} +ItemList::IconMode ItemList::get_icon_mode() const{ + + return icon_mode; +} + +void ItemList::set_min_icon_size(const Size2& p_size) { + + min_icon_size=p_size; + update(); +} + +Size2 ItemList::get_min_icon_size() const { + + return min_icon_size; +} + + + +void ItemList::_input_event(const InputEvent& p_event) { + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==BUTTON_LEFT && p_event.mouse_button.pressed) { + + const InputEventMouseButton &mb = p_event.mouse_button; + + search_string=""; //any mousepress cancels + Vector2 pos(mb.x,mb.y); + Ref<StyleBox> bg = get_stylebox("bg"); + pos-=bg->get_offset(); + pos.y+=scroll_bar->get_val(); + + int closest = -1; + int closest_dist=0x7FFFFFFF; + + for(int i=0;i<items.size();i++) { + + Rect2 rc = items[i].rect_cache; + if (i%current_columns==current_columns-1) { + rc.size.width=get_size().width; //not right but works + } + + if (rc.has_point(pos)) { + closest=i; + break; + } + + float dist = rc.distance_to(pos); + if (dist<closest_dist) { + closest=i; + closest_dist=dist; + } + } + + if (closest!=-1) { + + int i = closest; + + if (select_mode==SELECT_MULTI && items[i].selected && mb.mod.command) { + unselect(i); + emit_signal("multi_selected",i,false); + } else if (select_mode==SELECT_MULTI && mb.mod.shift && current>=0 && current<items.size() && current!=i) { + + int from = current; + int to = i; + if (i<current) { + SWAP(from,to); + } + for(int j=from;j<=to;j++) { + bool selected = !items[j].selected; + select(j,false); + if (selected) + emit_signal("multi_selected",i,true); + } + } else { + bool selected = !items[i].selected; + select(i,select_mode==SELECT_SINGLE || !mb.mod.command); + if (selected) { + if (select_mode==SELECT_SINGLE) { + emit_signal("item_selected",i); + } else + emit_signal("multi_selected",i,true); + } + + if (/*select_mode==SELECT_SINGLE &&*/ mb.doubleclick) { + + emit_signal("item_activated",i); + + } + + + } + + + return; + } + } + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==BUTTON_WHEEL_UP && p_event.mouse_button.pressed) { + + scroll_bar->set_val( scroll_bar->get_val()-scroll_bar->get_page()/8 ); + + } + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.button_index==BUTTON_WHEEL_DOWN && p_event.mouse_button.pressed) { + + scroll_bar->set_val( scroll_bar->get_val()+scroll_bar->get_page()/8 ); + + } + + if (p_event.is_pressed() && items.size()>0) { + if (p_event.is_action("ui_up")) { + + if (search_string!="") { + + uint64_t now = OS::get_singleton()->get_ticks_msec(); + uint64_t diff = now-search_time_msec; + + if (diff<int(Globals::get_singleton()->get("gui/incr_search_max_interval_msec"))*2) { + + for(int i=current-1;i>=0;i--) { + + if (items[i].text.begins_with(search_string)) { + + set_current(i); + ensure_current_is_visible(); + if (select_mode==SELECT_SINGLE) { + emit_signal("item_selected",current); + } + + + break; + } + } + accept_event(); + return; + } + } + + if (current>=current_columns) { + set_current(current-current_columns); + ensure_current_is_visible(); + if (select_mode==SELECT_SINGLE) { + emit_signal("item_selected",current); + } + accept_event(); + } + } else if (p_event.is_action("ui_down")) { + + if (search_string!="") { + + uint64_t now = OS::get_singleton()->get_ticks_msec(); + uint64_t diff = now-search_time_msec; + + if (diff<int(Globals::get_singleton()->get("gui/incr_search_max_interval_msec"))*2) { + + for(int i=current+1;i<items.size();i++) { + + if (items[i].text.begins_with(search_string)) { + + set_current(i); + ensure_current_is_visible(); + if (select_mode==SELECT_SINGLE) { + emit_signal("item_selected",current); + } + break; + } + } + accept_event(); + return; + } + } + + if (current<items.size()-current_columns) { + set_current(current+current_columns); + ensure_current_is_visible(); + if (select_mode==SELECT_SINGLE) { + emit_signal("item_selected",current); + } + accept_event(); + + } + } else if (p_event.is_action("ui_page_up")) { + + search_string=""; //any mousepress cancels + + for(int i=4;i>0;i--) { + if (current-current_columns*i >=0 ) { + set_current( current- current_columns*i); + ensure_current_is_visible(); + if (select_mode==SELECT_SINGLE) { + emit_signal("item_selected",current); + } + accept_event(); + break; + } + } + } else if (p_event.is_action("ui_page_down")) { + + search_string=""; //any mousepress cancels + + for(int i=4;i>0;i--) { + if (current+current_columns*i < items.size() ) { + set_current( current+ current_columns*i); + ensure_current_is_visible(); + if (select_mode==SELECT_SINGLE) { + emit_signal("item_selected",current); + } + accept_event(); + + break; + } + } + } else if (p_event.is_action("ui_left")) { + + search_string=""; //any mousepress cancels + + if (current%current_columns!=0) { + set_current(current-1); + ensure_current_is_visible(); + if (select_mode==SELECT_SINGLE) { + emit_signal("item_selected",current); + } + accept_event(); + + } + } else if (p_event.is_action("ui_right")) { + + search_string=""; //any mousepress cancels + + if (current%current_columns!=(current_columns-1)) { + set_current(current+1); + ensure_current_is_visible(); + if (select_mode==SELECT_SINGLE) { + emit_signal("item_selected",current); + } + accept_event(); + + } + } else if (p_event.is_action("ui_cancel")) { + search_string=""; + } else if (p_event.is_action("ui_select")) { + + + if (select_mode==SELECT_MULTI && current>=0 && current<items.size()) { + if (items[current].selectable && !items[current].selected) { + select(current,false); + emit_signal("multi_selected",current,true); + } else if (items[current].selected) { + unselect(current); + emit_signal("multi_selected",current,false); + } + } + } else if (p_event.is_action("ui_accept")) { + search_string=""; //any mousepress cance + + if (current>=0 && current<items.size()) { + emit_signal("item_activated",current); + } + } else if (p_event.type==InputEvent::KEY) { + + if (p_event.key.unicode) { + + uint64_t now = OS::get_singleton()->get_ticks_msec(); + uint64_t diff = now-search_time_msec; + uint64_t max_interval = uint64_t(GLOBAL_DEF("gui/incr_search_max_interval_msec",2000)); + search_time_msec = now; + + if (diff>max_interval) { + search_string=""; + } + + search_string+=String::chr(p_event.key.unicode); + for(int i=0;i<items.size();i++) { + if (items[i].text.begins_with(search_string)) { + set_current(i); + ensure_current_is_visible(); + if (select_mode==SELECT_SINGLE) { + emit_signal("item_selected",current); + } + break; + } + } + + } + + } + } + + + + +} + +void ItemList::ensure_current_is_visible() { + + ensure_selected_visible=true; + update(); +} + +void ItemList::_notification(int p_what) { + + if (p_what==NOTIFICATION_RESIZED) { + shape_changed=true; + update(); + } + + if (p_what==NOTIFICATION_DRAW) { + + VS::get_singleton()->canvas_item_set_clip(get_canvas_item(),true); + Ref<StyleBox> bg = get_stylebox("bg"); + + int mw = scroll_bar->get_minimum_size().x; + scroll_bar->set_anchor_and_margin(MARGIN_LEFT,ANCHOR_END,mw+bg->get_margin(MARGIN_RIGHT)); + scroll_bar->set_anchor_and_margin(MARGIN_RIGHT,ANCHOR_END,bg->get_margin(MARGIN_RIGHT)); + scroll_bar->set_anchor_and_margin(MARGIN_TOP,ANCHOR_BEGIN,bg->get_margin(MARGIN_TOP)); + scroll_bar->set_anchor_and_margin(MARGIN_BOTTOM,ANCHOR_END,bg->get_margin(MARGIN_BOTTOM)); + + + Size2 size = get_size(); + + float page = size.height-bg->get_minimum_size().height; + int width = size.width - mw - bg->get_minimum_size().width; + scroll_bar->set_page(page); + + draw_style_box(bg,Rect2(Point2(),size)); + + int hseparation = get_constant("hseparation"); + int vseparation = get_constant("vseparation"); + int icon_margin = get_constant("icon_margin"); + int line_separation = get_constant("line_separation"); + + Ref<StyleBox> sbsel = has_focus()?get_stylebox("selected_focus"):get_stylebox("selected"); + Ref<StyleBox> cursor = has_focus()?get_stylebox("cursor"):get_stylebox("cursor_unfocused"); + + Ref<Font> font = get_font("font"); + Color guide_color = get_color("guide_color"); + Color font_color = get_color("font_color"); + Color font_color_selected = get_color("font_color_selected"); + int font_height = font->get_height(); + Vector<int> line_size_cache; + Vector<int> line_limit_cache; + + if (max_text_lines) { + line_size_cache.resize(max_text_lines); + line_limit_cache.resize(max_text_lines); + } + + if (has_focus()) { + VisualServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(),true); + draw_style_box(get_stylebox("bg_focus"),Rect2(Point2(),size)); + VisualServer::get_singleton()->canvas_item_add_clip_ignore(get_canvas_item(),false); + } + + if (shape_changed) { + + //1- compute item minimum sizes + for(int i=0;i<items.size();i++) { + + Size2 minsize; + if (items[i].icon.is_valid()) { + minsize=items[i].icon->get_size(); + if (min_icon_size.x!=0) + minsize.x = MAX(minsize.x,min_icon_size.x); + if (min_icon_size.y!=0) + minsize.y = MAX(minsize.y,min_icon_size.y); + + if (items[i].text!="") { + if (icon_mode==ICON_MODE_TOP) { + minsize.y+=icon_margin; + } else { + minsize.x+=icon_margin; + } + } + } + + if (items[i].text!="") { + + Size2 s = font->get_string_size(items[i].text); + //s.width=MIN(s.width,fixed_column_width); + + + + if (icon_mode==ICON_MODE_TOP) { + minsize.x=MAX(minsize.x,s.width); + if (max_text_lines>0) { + minsize.y+=(font_height+line_separation)*max_text_lines; + } else { + minsize.y+=s.height; + } + + } else { + minsize.y=MAX(minsize.y,s.height); + minsize.x+=s.width; + } + } + + + + items[i].rect_cache.size=minsize; + if (fixed_column_width>0) + items[i].rect_cache.size.x=fixed_column_width; + + } + + int fit_size = size.x - bg->get_minimum_size().width - mw; + + //2-attempt best fit + current_columns = 0x7FFFFFFF; + if (max_columns>0) + current_columns=max_columns; + + + while(true) { + //repeat util all fits + //print_line("try with "+itos(current_columns)); + bool all_fit=true; + Vector2 ofs; + int col=0; + int max_h=0; + separators.clear();; + for(int i=0;i<items.size();i++) { + + if (current_columns>1 && items[i].rect_cache.size.width+ofs.x > fit_size) { + //went past + current_columns=MAX(col,1); + all_fit=false; + break; + } + + items[i].rect_cache.pos=ofs; + max_h=MAX(max_h,items[i].rect_cache.size.y); + ofs.x+=items[i].rect_cache.size.x; + //print_line("item "+itos(i)+" ofs "+rtos(items[i].rect_cache.size.x)); + if (col>0) + ofs.x+=hseparation; + col++; + if (col==current_columns) { + + if (i<items.size()-1) + separators.push_back(ofs.y+max_h+vseparation/2); + ofs.x=0; + ofs.y+=max_h+vseparation; + col=0; + max_h=0; + } + } + + if (all_fit) { + float max = MAX(page,ofs.y+max_h); + scroll_bar->set_max(max); + //print_line("max: "+rtos(max)+" page "+rtos(page)); + if (max<=page) { + scroll_bar->set_val(0); + scroll_bar->hide(); + } else { + scroll_bar->show(); + } + break; + } + } + + + shape_changed=false; + } + + + + Vector2 base_ofs = bg->get_offset(); + base_ofs.y-=int(scroll_bar->get_val()); + + Rect2 clip(Point2(),size-bg->get_minimum_size()+Vector2(0,scroll_bar->get_val())); + + for(int i=0;i<items.size();i++) { + + + Rect2 rcache = items[i].rect_cache; + + if (!clip.intersects(rcache)) + continue; + + + if (current_columns==1) { + rcache.size.width = width-rcache.pos.x; + } + if (items[i].custom_bg.a>0.001) { + Rect2 r=rcache; + r.pos+=base_ofs; + draw_rect(r,items[i].custom_bg); + } + if (items[i].selected) { + Rect2 r=rcache; + r.pos+=base_ofs; + + r.pos.x-=sbsel->get_margin(MARGIN_LEFT); + r.size.x+=sbsel->get_margin(MARGIN_LEFT)+sbsel->get_margin(MARGIN_RIGHT); + r.pos.y-=sbsel->get_margin(MARGIN_TOP); + r.size.y+=sbsel->get_margin(MARGIN_TOP)+sbsel->get_margin(MARGIN_BOTTOM); + + draw_style_box(sbsel,r); + + } + + + Vector2 text_ofs; + if (items[i].icon.is_valid()) { + + Vector2 icon_ofs; + if (min_icon_size!=Vector2()) { + icon_ofs = (min_icon_size - items[i].icon->get_size())/2; + } + + if (icon_mode==ICON_MODE_TOP) { + draw_texture(items[i].icon,icon_ofs+items[i].rect_cache.pos+Vector2(items[i].rect_cache.size.width/2-items[i].icon->get_width()/2,0).floor()+base_ofs); + text_ofs.y = MAX(items[i].icon->get_height(),min_icon_size.y)+icon_margin; + } else { + draw_texture(items[i].icon,icon_ofs+items[i].rect_cache.pos+Vector2(0,items[i].rect_cache.size.height/2-items[i].icon->get_height()/2).floor()+base_ofs); + text_ofs.x = MAX(items[i].icon->get_width(),min_icon_size.x)+icon_margin; + } + } + + if (items[i].tag_icon.is_valid()) { + + draw_texture(items[i].tag_icon,items[i].rect_cache.pos+base_ofs); + } + + if (items[i].text!="") { + + int max_len=-1; + + Vector2 size = font->get_string_size(items[i].text); + if (fixed_column_width) + max_len=fixed_column_width; + else + max_len=size.x; + + if (icon_mode==ICON_MODE_TOP && max_text_lines>0) { + + int ss = items[i].text.length(); + float ofs=0; + int line=0; + for(int j=0;j<=ss;j++) { + + int cs = j<ss?font->get_char_size(items[i].text[j],items[i].text[j+1]).x:0; + if (ofs+cs>max_len || j==ss) { + line_limit_cache[line]=j; + line_size_cache[line]=ofs; + line++; + ofs=0; + if (line>=max_text_lines) + break; + } else { + ofs+=cs; + } + + } + + line=0; + ofs=0; + + text_ofs.y+=font->get_ascent(); + text_ofs=text_ofs.floor(); + text_ofs+=base_ofs; + text_ofs+=items[i].rect_cache.pos; + + for(int j=0;j<ss;j++) { + + if (j==line_limit_cache[line]) { + line++; + ofs=0; + if (line>=max_text_lines) + break; + } + ofs+=font->draw_char(get_canvas_item(),text_ofs+Vector2(ofs+(max_len-line_size_cache[line])/2,line*(font_height+line_separation)).floor(),items[i].text[j],items[i].text[j+1],items[i].selected?font_color_selected:font_color); + } + + //special multiline mode + } else { + + if (fixed_column_width>0) + size.x=MIN(size.x,fixed_column_width); + + if (icon_mode==ICON_MODE_TOP) { + text_ofs.x+=(items[i].rect_cache.size.width-size.x)/2; + } else { + text_ofs.y+=(items[i].rect_cache.size.height-size.y)/2; + } + + text_ofs.y+=font->get_ascent(); + text_ofs=text_ofs.floor(); + text_ofs+=base_ofs; + text_ofs+=items[i].rect_cache.pos; + + draw_string(font,text_ofs,items[i].text,items[i].selected?font_color_selected:font_color,max_len+1); + } + + + } + + if (select_mode==SELECT_MULTI && i==current) { + + Rect2 r=rcache; + r.pos+=base_ofs; + draw_style_box(cursor,r); + + } + } + + for(int i=0;i<separators.size();i++) { + draw_line(Vector2(bg->get_margin(MARGIN_LEFT),base_ofs.y+separators[i]),Vector2(size.width-bg->get_margin(MARGIN_LEFT),base_ofs.y+separators[i]),guide_color); + } + + + if (ensure_selected_visible && current>=0 && current <=items.size()) { + + Rect2 r = items[current].rect_cache; + int from = scroll_bar->get_val(); + int to = from + scroll_bar->get_page(); + + if (r.pos.y < from) { + scroll_bar->set_val(r.pos.y); + } else if (r.pos.y+r.size.y > to) { + scroll_bar->set_val(r.pos.y+r.size.y - (to-from)); + } + + + } + + ensure_selected_visible=false; + + } +} + +void ItemList::_scroll_changed(double) { + update(); +} + + +String ItemList::get_tooltip(const Point2& p_pos) const { + + Vector2 pos=p_pos; + Ref<StyleBox> bg = get_stylebox("bg"); + pos-=bg->get_offset(); + pos.y+=scroll_bar->get_val(); + + int closest = -1; + int closest_dist=0x7FFFFFFF; + + for(int i=0;i<items.size();i++) { + + Rect2 rc = items[i].rect_cache; + if (i%current_columns==current_columns-1) { + rc.size.width=get_size().width; //not right but works + } + + if (rc.has_point(pos)) { + closest=i; + break; + } + + float dist = rc.distance_to(pos); + if (dist<closest_dist) { + closest=i; + closest_dist=dist; + } + } + + if (closest!=-1) { + if (items[closest].tooltip!="") { + return items[closest].tooltip; + } + if (items[closest].text!="") { + return items[closest].text; + } + } + + return Control::get_tooltip(p_pos); + + +} + +void ItemList::sort_items_by_text() { + items.sort(); + update(); + if (select_mode==SELECT_SINGLE) { + for(int i=0;i<items.size();i++) { + if (items[i].selected) { + select(i); + return; + } + } + } +} + +int ItemList::find_metadata(const Variant& p_metadata) const { + + for(int i=0;i<items.size();i++) { + if (items[i].metadata==p_metadata) { + return i; + } + } + + return -1; + +} + +void ItemList::_bind_methods(){ + + ObjectTypeDB::bind_method(_MD("add_item","text","icon:Texture","selectable"),&ItemList::add_item,DEFVAL(Ref<Texture>()),DEFVAL(true)); + ObjectTypeDB::bind_method(_MD("add_icon_item","icon:Texture","selectable"),&ItemList::add_icon_item,DEFVAL(true)); + + ObjectTypeDB::bind_method(_MD("set_item_text","idx","text"),&ItemList::set_item_text); + ObjectTypeDB::bind_method(_MD("get_item_text","idx"),&ItemList::get_item_text); + + ObjectTypeDB::bind_method(_MD("set_item_icon","idx","icon:Texture"),&ItemList::set_item_icon); + ObjectTypeDB::bind_method(_MD("get_item_icon:Texture","idx"),&ItemList::get_item_icon); + + ObjectTypeDB::bind_method(_MD("set_item_selectable","idx","selectable"),&ItemList::set_item_selectable); + ObjectTypeDB::bind_method(_MD("is_item_selectable","idx"),&ItemList::is_item_selectable); + + ObjectTypeDB::bind_method(_MD("set_item_disabled","idx","disabled"),&ItemList::set_item_disabled); + ObjectTypeDB::bind_method(_MD("is_item_disabled","idx"),&ItemList::is_item_disabled); + + ObjectTypeDB::bind_method(_MD("set_item_metadata","idx","metadata"),&ItemList::set_item_metadata); + ObjectTypeDB::bind_method(_MD("get_item_metadata","idx"),&ItemList::get_item_metadata); + + ObjectTypeDB::bind_method(_MD("set_item_custom_bg_color","idx","custom_bg_color"),&ItemList::set_item_custom_bg_color); + ObjectTypeDB::bind_method(_MD("get_item_custom_bg_color","idx"),&ItemList::get_item_custom_bg_color); + + ObjectTypeDB::bind_method(_MD("set_item_tooltip","idx","tooltip"),&ItemList::set_item_tooltip); + ObjectTypeDB::bind_method(_MD("get_item_tooltip","idx"),&ItemList::get_item_tooltip); + + ObjectTypeDB::bind_method(_MD("select","idx","single"),&ItemList::select,DEFVAL(true)); + ObjectTypeDB::bind_method(_MD("unselect","idx"),&ItemList::unselect); + ObjectTypeDB::bind_method(_MD("is_selected","idx"),&ItemList::is_selected); + + ObjectTypeDB::bind_method(_MD("get_item_count"),&ItemList::get_item_count); + ObjectTypeDB::bind_method(_MD("remove_item","idx"),&ItemList::remove_item); + + ObjectTypeDB::bind_method(_MD("clear"),&ItemList::clear); + ObjectTypeDB::bind_method(_MD("sort_items_by_text"),&ItemList::clear); + + ObjectTypeDB::bind_method(_MD("set_fixed_column_width","width"),&ItemList::set_fixed_column_width); + ObjectTypeDB::bind_method(_MD("get_fixed_column_width"),&ItemList::get_fixed_column_width); + + ObjectTypeDB::bind_method(_MD("set_max_text_lines","lines"),&ItemList::set_max_text_lines); + ObjectTypeDB::bind_method(_MD("get_max_text_lines"),&ItemList::get_max_text_lines); + + ObjectTypeDB::bind_method(_MD("set_max_columns","amount"),&ItemList::set_max_columns); + ObjectTypeDB::bind_method(_MD("get_max_columns"),&ItemList::get_max_columns); + + ObjectTypeDB::bind_method(_MD("set_select_mode","mode"),&ItemList::set_select_mode); + ObjectTypeDB::bind_method(_MD("get_select_mode"),&ItemList::get_select_mode); + + ObjectTypeDB::bind_method(_MD("set_icon_mode","mode"),&ItemList::set_icon_mode); + ObjectTypeDB::bind_method(_MD("get_icon_mode"),&ItemList::get_icon_mode); + + ObjectTypeDB::bind_method(_MD("set_min_icon_size","size"),&ItemList::set_min_icon_size); + ObjectTypeDB::bind_method(_MD("get_min_icon_size"),&ItemList::get_min_icon_size); + + ObjectTypeDB::bind_method(_MD("ensure_current_is_visible"),&ItemList::ensure_current_is_visible); + + ObjectTypeDB::bind_method(_MD("_scroll_changed"),&ItemList::_scroll_changed); + ObjectTypeDB::bind_method(_MD("_input_event"),&ItemList::_input_event); + + BIND_CONSTANT( ICON_MODE_TOP ); + BIND_CONSTANT( ICON_MODE_LEFT ); + BIND_CONSTANT( SELECT_SINGLE ); + BIND_CONSTANT( SELECT_MULTI ); + + ADD_SIGNAL( MethodInfo("item_selected",PropertyInfo(Variant::INT,"index"))); + ADD_SIGNAL( MethodInfo("multi_selected",PropertyInfo(Variant::INT,"index"),PropertyInfo(Variant::BOOL,"selected"))); + ADD_SIGNAL( MethodInfo("item_activated",PropertyInfo(Variant::INT,"index"))); +} + + + +ItemList::ItemList() { + + current=-1; + + select_mode=SELECT_SINGLE; + icon_mode=ICON_MODE_LEFT; + + fixed_column_width=0; + max_text_lines=1; + max_columns=1; + + scroll_bar = memnew( VScrollBar ); + add_child(scroll_bar); + + shape_changed=true; + scroll_bar->connect("value_changed",this,"_scroll_changed"); + + set_focus_mode(FOCUS_ALL); + current_columns=1; + search_time_msec=0; + ensure_selected_visible=false; + +} + +ItemList::~ItemList() { + +} + diff --git a/scene/gui/item_list.h b/scene/gui/item_list.h new file mode 100644 index 0000000000..bd3cf6484e --- /dev/null +++ b/scene/gui/item_list.h @@ -0,0 +1,144 @@ +#ifndef ITEMLIST_H +#define ITEMLIST_H + +#include "scene/gui/control.h" +#include "scene/gui/scroll_bar.h" + +class ItemList : public Control { + + OBJ_TYPE( ItemList, Control ); +public: + + enum IconMode { + ICON_MODE_TOP, + ICON_MODE_LEFT + }; + + enum SelectMode { + SELECT_SINGLE, + SELECT_MULTI + }; +private: + struct Item { + + Ref<Texture> icon; + Ref<Texture> tag_icon; + String text; + bool selectable; + bool selected; + bool disabled; + Variant metadata; + String tooltip; + Color custom_bg; + + + Rect2 rect_cache; + + bool operator<(const Item& p_another) const { return text<p_another.text; } + }; + + int current; + + bool shape_changed; + + bool ensure_selected_visible; + + Vector<Item> items; + Vector<int> separators; + + SelectMode select_mode; + IconMode icon_mode; + VScrollBar *scroll_bar; + + uint64_t search_time_msec; + String search_string; + + int current_columns; + int fixed_column_width; + int max_text_lines; + int max_columns; + Size2 min_icon_size; + + void _scroll_changed(double); + void _input_event(const InputEvent& p_event); +protected: + + void _notification(int p_what); + static void _bind_methods(); +public: + + void add_item(const String& p_item,const Ref<Texture>& p_texture=Ref<Texture>(),bool p_selectable=true); + void add_icon_item(const Ref<Texture>& p_item,bool p_selectable=true); + + void set_item_text(int p_idx,const String& p_text); + String get_item_text(int p_idx) const; + + void set_item_icon(int p_idx,const Ref<Texture>& p_icon); + Ref<Texture> get_item_icon(int p_idx) const; + + void set_item_selectable(int p_idx,bool p_selectable); + bool is_item_selectable(int p_idx) const; + + void set_item_disabled(int p_idx,bool p_disabled); + bool is_item_disabled(int p_idx) const; + + void set_item_metadata(int p_idx,const Variant& p_metadata); + Variant get_item_metadata(int p_idx) const; + + void set_item_tag_icon(int p_idx,const Ref<Texture>& p_tag_icon); + Ref<Texture> get_item_tag_icon(int p_idx) const; + + void set_item_tooltip(int p_idx,const String& p_tooltip); + String get_item_tooltip(int p_idx) const; + + void set_item_custom_bg_color(int p_idx,const Color& p_custom_bg_color); + Color get_item_custom_bg_color(int p_idx) const; + + void select(int p_idx,bool p_single=true); + void unselect(int p_idx); + bool is_selected(int p_idx) const; + + void set_current(int p_current); + int get_current() const; + + void move_item(int p_item,int p_to_pos); + + int get_item_count() const; + void remove_item(int p_idx); + + void clear(); + + void set_fixed_column_width(int p_size); + int get_fixed_column_width() const; + + void set_max_text_lines(int p_amount); + int get_max_text_lines() const; + + void set_max_columns(int p_amount); + int get_max_columns() const; + + void set_select_mode(SelectMode p_mode); + SelectMode get_select_mode() const; + + void set_icon_mode(IconMode p_mode); + IconMode get_icon_mode() const; + + void set_min_icon_size(const Size2& p_size); + Size2 get_min_icon_size() const; + + void ensure_current_is_visible(); + + void sort_items_by_text(); + int find_metadata(const Variant& p_metadata) const; + + virtual String get_tooltip(const Point2& p_pos) const; + + ItemList(); + ~ItemList(); +}; + +VARIANT_ENUM_CAST(ItemList::SelectMode); +VARIANT_ENUM_CAST(ItemList::IconMode); + + +#endif // ITEMLIST_H diff --git a/scene/gui/label.cpp b/scene/gui/label.cpp index d2e1e7f0b9..002e49cbcf 100644 --- a/scene/gui/label.cpp +++ b/scene/gui/label.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -33,15 +33,15 @@ void Label::set_autowrap(bool p_autowrap) { - + autowrap=p_autowrap; word_cache_dirty=true; minimum_size_changed(); - - + update(); + } bool Label::has_autowrap() const { - + return autowrap; } @@ -51,6 +51,7 @@ void Label::set_uppercase(bool p_uppercase) { uppercase=p_uppercase; word_cache_dirty=true; minimum_size_changed(); + update(); } bool Label::is_uppercase() const { @@ -66,18 +67,18 @@ int Label::get_line_height() const { void Label::_notification(int p_what) { - + if (p_what==NOTIFICATION_DRAW) { - - if (clip && !autowrap) + + if (clip || autowrap) VisualServer::get_singleton()->canvas_item_set_clip(get_canvas_item(),true); if (word_cache_dirty) regenerate_word_cache(); - + RID ci = get_canvas_item(); - + Size2 string_size; Size2 size=get_size(); @@ -87,40 +88,46 @@ void Label::_notification(int p_what) { bool use_outlinde = get_constant("shadow_as_outline"); Point2 shadow_ofs(get_constant("shadow_offset_x"),get_constant("shadow_offset_y")); - + VisualServer::get_singleton()->canvas_item_set_distance_field_mode(get_canvas_item(),font.is_valid() && font->is_distance_field_hint()); + int font_h = font->get_height(); - int line_from=(int)get_val(); // + p_exposed.pos.y / font_h; int lines_visible = size.y/font_h; - int line_to=(int)get_val() + lines_visible; //p_exposed.pos.y+p_exposed.size.height / font_h; int space_w=font->get_char_size(' ').width; - int lines_total = get_max(); int chars_total=0; int vbegin=0,vsep=0; - if (lines_total && lines_total < lines_visible) { + if (lines_visible > line_count) { + lines_visible = line_count; + + } + + if (max_lines_visible >= 0 && lines_visible > max_lines_visible) { + lines_visible = max_lines_visible; + } + + if (lines_visible > 0) { switch(valign) { case VALIGN_TOP: { - //nothing } break; case VALIGN_CENTER: { - - vbegin=(lines_visible-lines_total) * font_h / 2; + vbegin=(size.y - lines_visible * font_h) / 2; vsep=0; + } break; case VALIGN_BOTTOM: { - vbegin=(lines_visible-lines_total) * font_h; + vbegin=size.y - lines_visible * font_h; vsep=0; } break; case VALIGN_FILL: { vbegin=0; - if (lines_total>1) { - vsep=(lines_visible-lines_total) * font_h / (lines_total-1); + if (lines_visible>1) { + vsep=(size.y - lines_visible * font_h) / (lines_visible - 1); } else { vsep=0; } @@ -128,21 +135,21 @@ void Label::_notification(int p_what) { } break; } } - - + + WordCache *wc = word_cache; if (!wc) return; - + int c = 0; int line=0; + int line_to=lines_skipped + (lines_visible>0?lines_visible:1); while(wc) { - /* handle lines not meant to be drawn quickly */ - if (line>line_to) + if (line>=line_to) break; - if (line<line_from) { - + if (line<lines_skipped) { + while (wc && wc->char_pos>=0) wc=wc->next; if (wc) @@ -150,36 +157,36 @@ void Label::_notification(int p_what) { line++; continue; } - + /* handle lines normally */ - + if (wc->char_pos<0) { //empty line wc=wc->next; line++; continue; } - + WordCache *from=wc; WordCache *to=wc; - + int taken=0; int spaces=0; while(to && to->char_pos>=0) { - + taken+=to->pixel_width; - if (to!=from) { - spaces++; + if (to!=from && to->space_count) { + spaces+=to->space_count; } to=to->next; } - + bool can_fill = to && to->char_pos==WordCache::CHAR_WRAPLINE; float x_ofs=0; - + switch (align) { - + case ALIGN_FILL: case ALIGN_LEFT: { @@ -197,38 +204,38 @@ void Label::_notification(int p_what) { } break; } - - int y_ofs=(line-(int)get_val())*font_h + font->get_ascent(); + + int y_ofs=(line-lines_skipped)*font_h + font->get_ascent(); y_ofs+=vbegin + line*vsep; - + while(from!=to) { - + // draw a word int pos = from->char_pos; if (from->char_pos<0) { - + ERR_PRINT("BUG"); return; } - if (from!=wc) { + if (from->space_count) { /* spacing */ - x_ofs+=space_w; + x_ofs+=space_w*from->space_count; if (can_fill && align==ALIGN_FILL && spaces) { - + x_ofs+=int((size.width-(taken+space_w*spaces))/spaces); } - - + + } - - - + + + if (font_color_shadow.a>0) { - + int chars_total_shadow = chars_total; //save chars drawn float x_ofs_shadow=x_ofs; for (int i=0;i<from->word_len;i++) { - + if (visible_chars < 0 || chars_total_shadow<visible_chars) { CharType c = text[i+pos]; CharType n = text[i+pos+1]; @@ -248,10 +255,10 @@ void Label::_notification(int p_what) { } } - + } for (int i=0;i<from->word_len;i++) { - + if (visible_chars < 0 || chars_total<visible_chars) { CharType c = text[i+pos]; CharType n = text[i+pos+1]; @@ -267,73 +274,73 @@ void Label::_notification(int p_what) { } from=from->next; } - + wc=to?to->next:0; line++; - - } + + } } - + if (p_what==NOTIFICATION_THEME_CHANGED) { word_cache_dirty=true; update(); } if (p_what==NOTIFICATION_RESIZED) { - + word_cache_dirty=true; } - + } Size2 Label::get_minimum_size() const { - + if (autowrap) return Size2(1,1); else { - + // don't want to mutable everything - if(word_cache_dirty) + if(word_cache_dirty) const_cast<Label*>(this)->regenerate_word_cache(); - + Size2 ms=minsize; if (clip) ms.width=1; return ms; - } + } } int Label::get_longest_line_width() const { - + Ref<Font> font = get_font("font"); int max_line_width=0; int line_width=0; - - for (int i=0;i<text.size()+1;i++) { - - CharType current=i<text.length()?text[i]:' '; //always a space at the end, so the algo works + + for (int i=0;i<text.size();i++) { + + CharType current=text[i]; if (uppercase) current=String::char_uppercase(current); if (current<32) { - + if (current=='\n') { - + if (line_width>max_line_width) max_line_width=line_width; line_width=0; } } else { - + int char_width=font->get_char_size(current).width; - line_width+=char_width; + line_width+=char_width; } - + } if (line_width>max_line_width) max_line_width=line_width; - + return max_line_width; } @@ -348,40 +355,46 @@ int Label::get_line_count() const { } void Label::regenerate_word_cache() { - + while (word_cache) { - + WordCache *current=word_cache; word_cache=current->next; memdelete( current ); } - - + + int width=autowrap?get_size().width:get_longest_line_width(); Ref<Font> font = get_font("font"); - + int current_word_size=0; int word_pos=0; int line_width=0; - int last_width=0; + int space_count=0; + int space_width=font->get_char_size(' ').width; line_count=1; total_char_cache=0; - + WordCache *last=NULL; - + for (int i=0;i<text.size()+1;i++) { - + CharType current=i<text.length()?text[i]:' '; //always a space at the end, so the algo works - + if (uppercase) current=String::char_uppercase(current); + // ranges taken from http://www.unicodemap.org/ + // if your language is not well supported, consider helping improve + // the unicode support in Godot. + bool separatable = (current>=0x2E08 && current<=0xFAFF) || (current>=0xFE30 && current<=0xFE4F); + //current>=33 && (current < 65||current >90) && (current<97||current>122) && (current<48||current>57); bool insert_newline=false; - + int char_width; + if (current<33) { - + if (current_word_size>0) { - WordCache *wc = memnew( WordCache ); if (word_cache) { last->next=wc; @@ -389,14 +402,16 @@ void Label::regenerate_word_cache() { word_cache=wc; } last=wc; - + wc->pixel_width=current_word_size; wc->char_pos=word_pos; wc->word_len=i-word_pos; + wc->space_count = space_count; current_word_size=0; - + space_count=0; + } - + if (current=='\n') { insert_newline=true; @@ -406,26 +421,49 @@ void Label::regenerate_word_cache() { if (i<text.length() && text[i] == ' ') { total_char_cache--; // do not count spaces + if (line_width > 0 || last==NULL || last->char_pos!=WordCache::CHAR_WRAPLINE) { + space_count++; + line_width+=space_width; + }else { + space_count=0; + } } } else { - + // latin characters if (current_word_size==0) { - if (line_width>0) // add a space before the new word if a word existed before - line_width+=font->get_char_size(' ').width; word_pos=i; } - - int char_width=font->get_char_size(current).width; + + char_width=font->get_char_size(current).width; current_word_size+=char_width; line_width+=char_width; total_char_cache++; - + } - - if ((autowrap && line_width>=width && last_width<width) || insert_newline) { - + + if ((autowrap && (line_width >= width) && ((last && last->char_pos >= 0) || separatable)) || insert_newline) { + if (separatable) { + if (current_word_size>0) { + WordCache *wc = memnew( WordCache ); + if (word_cache) { + last->next=wc; + } else { + word_cache=wc; + } + last=wc; + + wc->pixel_width=current_word_size-char_width; + wc->char_pos=word_pos; + wc->word_len=i-word_pos; + wc->space_count = space_count; + current_word_size=char_width; + space_count=0; + word_pos=i; + } + } + WordCache *wc = memnew( WordCache ); if (word_cache) { last->next=wc; @@ -433,39 +471,30 @@ void Label::regenerate_word_cache() { word_cache=wc; } last=wc; - + wc->pixel_width=0; wc->char_pos=insert_newline?WordCache::CHAR_NEWLINE:WordCache::CHAR_WRAPLINE; line_width=current_word_size; line_count++; + space_count=0; - } - - last_width=line_width; - + } - - //total_char_cache -= line_count + 1; // do not count new lines (including the first one) - + if (!autowrap) { - minsize.width=width; - minsize.height=font->get_height()*line_count; - set_page( line_count ); - - } else { - - set_page( get_size().height / font->get_height() ); + if (max_lines_visible > 0 && line_count > max_lines_visible) { + minsize.height=font->get_height()*max_lines_visible; + } else { + minsize.height=font->get_height()*line_count; + } } - - set_max(line_count); - + word_cache_dirty=false; - -} +} void Label::set_align(Align p_align) { @@ -475,7 +504,7 @@ void Label::set_align(Align p_align) { } Label::Align Label::get_align() const{ - + return align; } @@ -492,24 +521,23 @@ Label::VAlign Label::get_valign() const{ } void Label::set_text(const String& p_string) { - + String str = XL_MESSAGE(p_string); if (text==str) return; - + text=str; word_cache_dirty=true; + if (percent_visible<1) + visible_chars=get_total_character_count()*percent_visible; update(); - if (!autowrap) - minimum_size_changed(); - + minimum_size_changed(); + } void Label::set_clip_text(bool p_clip) { - if (clip==p_clip) - return; clip=p_clip; update(); minimum_size_changed(); @@ -521,23 +549,39 @@ bool Label::is_clipping_text() const { } String Label::get_text() const { - + return text; } void Label::set_visible_characters(int p_amount) { visible_chars=p_amount; + if (get_total_character_count() > 0) { + percent_visible=(float)p_amount/(float)total_char_cache; + } update(); } +int Label::get_visible_characters() const { + + return visible_chars; +} + void Label::set_percent_visible(float p_percent) { - if (p_percent<0) - set_visible_characters(-1); - else - set_visible_characters(get_total_character_count()*p_percent); - percent_visible=p_percent; + if (p_percent<0 || p_percent>=1) { + + visible_chars=-1; + percent_visible=1; + + } else { + + visible_chars=get_total_character_count()*p_percent; + percent_visible=p_percent; + + } + update(); + } float Label::get_percent_visible() const{ @@ -545,6 +589,27 @@ float Label::get_percent_visible() const{ return percent_visible; } +void Label::set_lines_skipped(int p_lines) { + + lines_skipped=p_lines; + update(); +} + +int Label::get_lines_skipped() const{ + + return lines_skipped; +} + +void Label::set_max_lines_visible(int p_lines) { + + max_lines_visible=p_lines; + update(); +} + +int Label::get_max_lines_visible() const{ + + return max_lines_visible; +} int Label::get_total_character_count() const { @@ -555,7 +620,7 @@ int Label::get_total_character_count() const { } void Label::_bind_methods() { - + ObjectTypeDB::bind_method(_MD("set_align","align"),&Label::set_align); ObjectTypeDB::bind_method(_MD("get_align"),&Label::get_align); ObjectTypeDB::bind_method(_MD("set_valign","valign"),&Label::set_valign); @@ -564,14 +629,21 @@ void Label::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_text"),&Label::get_text); ObjectTypeDB::bind_method(_MD("set_autowrap","enable"),&Label::set_autowrap); ObjectTypeDB::bind_method(_MD("has_autowrap"),&Label::has_autowrap); + ObjectTypeDB::bind_method(_MD("set_clip_text","enable"),&Label::set_clip_text); + ObjectTypeDB::bind_method(_MD("is_clipping_text"),&Label::is_clipping_text); ObjectTypeDB::bind_method(_MD("set_uppercase","enable"),&Label::set_uppercase); ObjectTypeDB::bind_method(_MD("is_uppercase"),&Label::is_uppercase); ObjectTypeDB::bind_method(_MD("get_line_height"),&Label::get_line_height); ObjectTypeDB::bind_method(_MD("get_line_count"),&Label::get_line_count); ObjectTypeDB::bind_method(_MD("get_total_character_count"),&Label::get_total_character_count); - ObjectTypeDB::bind_method(_MD("set_visible_characters"),&Label::set_visible_characters); + ObjectTypeDB::bind_method(_MD("set_visible_characters","amount"),&Label::set_visible_characters); + ObjectTypeDB::bind_method(_MD("get_visible_characters"),&Label::get_visible_characters); ObjectTypeDB::bind_method(_MD("set_percent_visible","percent_visible"),&Label::set_percent_visible); ObjectTypeDB::bind_method(_MD("get_percent_visible"),&Label::get_percent_visible); + ObjectTypeDB::bind_method(_MD("set_lines_skipped","lines_skipped"),&Label::set_lines_skipped); + ObjectTypeDB::bind_method(_MD("get_lines_skipped"),&Label::get_lines_skipped); + ObjectTypeDB::bind_method(_MD("set_max_lines_visible","lines_visible"),&Label::set_max_lines_visible); + ObjectTypeDB::bind_method(_MD("get_max_lines_visible"),&Label::get_max_lines_visible); BIND_CONSTANT( ALIGN_LEFT ); BIND_CONSTANT( ALIGN_CENTER ); @@ -583,22 +655,25 @@ void Label::_bind_methods() { BIND_CONSTANT( VALIGN_BOTTOM ); BIND_CONSTANT( VALIGN_FILL ); - ADD_PROPERTY( PropertyInfo( Variant::STRING, "text",PROPERTY_HINT_MULTILINE_TEXT,"",PROPERTY_USAGE_DEFAULT_INTL), _SCS("set_text"),_SCS("get_text") ); - ADD_PROPERTY( PropertyInfo( Variant::INT, "align", PROPERTY_HINT_ENUM,"Left,Center,Right,Fill" ),_SCS("set_align"),_SCS("get_align") ); - ADD_PROPERTY( PropertyInfo( Variant::INT, "valign", PROPERTY_HINT_ENUM,"Top,Center,Bottom,Fill" ),_SCS("set_valign"),_SCS("get_valign") ); - ADD_PROPERTY( PropertyInfo( Variant::BOOL, "autowrap"),_SCS("set_autowrap"),_SCS("has_autowrap") ); - ADD_PROPERTY( PropertyInfo( Variant::BOOL, "uppercase"),_SCS("set_uppercase"),_SCS("is_uppercase") ); + ADD_PROPERTYNZ( PropertyInfo( Variant::STRING, "text",PROPERTY_HINT_MULTILINE_TEXT,"",PROPERTY_USAGE_DEFAULT_INTL), _SCS("set_text"),_SCS("get_text") ); + ADD_PROPERTYNZ( PropertyInfo( Variant::INT, "align", PROPERTY_HINT_ENUM,"Left,Center,Right,Fill" ),_SCS("set_align"),_SCS("get_align") ); + ADD_PROPERTYNZ( PropertyInfo( Variant::INT, "valign", PROPERTY_HINT_ENUM,"Top,Center,Bottom,Fill" ),_SCS("set_valign"),_SCS("get_valign") ); + ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "autowrap"),_SCS("set_autowrap"),_SCS("has_autowrap") ); + ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "clip_text"),_SCS("set_clip_text"),_SCS("is_clipping_text") ); + ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "uppercase"),_SCS("set_uppercase"),_SCS("is_uppercase") ); ADD_PROPERTY( PropertyInfo( Variant::REAL, "percent_visible", PROPERTY_HINT_RANGE,"0,1,0.001"),_SCS("set_percent_visible"),_SCS("get_percent_visible") ); + ADD_PROPERTY( PropertyInfo( Variant::INT, "lines_skipped", PROPERTY_HINT_RANGE,"0,999,1"),_SCS("set_lines_skipped"),_SCS("get_lines_skipped") ); + ADD_PROPERTY( PropertyInfo( Variant::INT, "max_lines_visible", PROPERTY_HINT_RANGE,"-1,999,1"),_SCS("set_max_lines_visible"),_SCS("get_max_lines_visible") ); } Label::Label(const String &p_text) { - + align=ALIGN_LEFT; valign=VALIGN_TOP; text=""; word_cache=NULL; - word_cache_dirty=true; + word_cache_dirty=true; autowrap=false; line_count=0; set_v_size_flags(0); @@ -606,20 +681,22 @@ Label::Label(const String &p_text) { set_ignore_mouse(true); total_char_cache=0; visible_chars=-1; - percent_visible=-1; + percent_visible=1; + lines_skipped=0; + max_lines_visible=-1; set_text(p_text); uppercase=false; } Label::~Label() { - + while (word_cache) { - + WordCache *current=word_cache; word_cache=current->next; memdelete( current ); - } + } } diff --git a/scene/gui/label.h b/scene/gui/label.h index 131ee3a944..4ea9f5d377 100644 --- a/scene/gui/label.h +++ b/scene/gui/label.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -29,17 +29,17 @@ #ifndef LABEL_H #define LABEL_H -#include "scene/gui/range.h" +#include "scene/gui/control.h" /** @author Juan Linietsky <reduzio@gmail.com> */ -class Label : public Range { - - OBJ_TYPE( Label, Range ); -public: - +class Label : public Control { + + OBJ_TYPE( Label, Control ); +public: + enum Align { - + ALIGN_LEFT, ALIGN_CENTER, ALIGN_RIGHT, @@ -63,11 +63,11 @@ private: Size2 minsize; int line_count; bool uppercase; - + int get_longest_line_width() const; - + struct WordCache { - + enum { CHAR_NEWLINE=-1, CHAR_WRAPLINE=-2 @@ -75,25 +75,28 @@ private: int char_pos; // if -1, then newline int word_len; int pixel_width; + int space_count; WordCache *next; - WordCache() { char_pos=0; word_len=0; pixel_width=0; next=0; } - }; - + WordCache() { char_pos=0; word_len=0; pixel_width=0; next=0; space_count=0;} + }; + bool word_cache_dirty; void regenerate_word_cache(); float percent_visible; - + WordCache *word_cache; int total_char_cache; int visible_chars; -protected: + int lines_skipped; + int max_lines_visible; +protected: void _notification(int p_what); static void _bind_methods(); // bind helpers public: - + virtual Size2 get_minimum_size() const; void set_align(Align p_align); @@ -104,7 +107,7 @@ public: void set_text(const String& p_string); String get_text() const; - + void set_autowrap(bool p_autowrap); bool has_autowrap() const; @@ -112,6 +115,7 @@ public: bool is_uppercase() const; void set_visible_characters(int p_amount); + int get_visible_characters() const; int get_total_character_count() const; void set_clip_text(bool p_clip); @@ -120,6 +124,11 @@ public: void set_percent_visible(float p_percent); float get_percent_visible() const; + void set_lines_skipped(int p_lines); + int get_lines_skipped() const; + + void set_max_lines_visible(int p_lines); + int get_max_lines_visible() const; int get_line_height() const; int get_line_count() const; diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 68c990033a..18de8ed568 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -145,6 +145,13 @@ void LineEdit::_input_event(InputEvent p_event) { int old_cursor_pos = cursor_pos; text = undo_text; + + Ref<Font> font = get_font("font"); + + cached_width = 0; + for (int i = 0; i<text.length(); i++) + cached_width += font->get_char_size(text[i]).width; + if(old_cursor_pos > text.length()) { set_cursor_pos(text.length()); } else { @@ -164,6 +171,15 @@ void LineEdit::_input_event(InputEvent p_event) { selection_clear(); undo_text = text; text = text.substr(cursor_pos,text.length()-cursor_pos); + + Ref<Font> font = get_font("font"); + + cached_width = 0; + if (font != NULL) { + for (int i = 0; i < text.length(); i++) + cached_width += font->get_char_size(text[i]).width; + } + set_cursor_pos(0); emit_signal("text_changed",text); _change_notify("text"); @@ -192,6 +208,9 @@ void LineEdit::_input_event(InputEvent p_event) { } } break; + case (KEY_A): { //Select All + select(); + } break; default: { handled=false;} } @@ -272,7 +291,7 @@ void LineEdit::_input_event(InputEvent p_event) { if (editable) { selection_delete(); - CharType ucodestr[2]={k.unicode,0}; + CharType ucodestr[2]={(CharType)k.unicode,0}; append_at_cursor(ucodestr); emit_signal("text_changed",text); _change_notify("text"); @@ -303,6 +322,18 @@ void LineEdit::_input_event(InputEvent p_event) { } } +void LineEdit::set_align(Align p_align) { + + ERR_FAIL_INDEX(p_align, 4); + align = p_align; + update(); +} + +LineEdit::Align LineEdit::get_align() const{ + + return align; +} + Variant LineEdit::get_drag_data(const Point2& p_point) { if (selection.drag_attempt && selection.enabled) { @@ -325,7 +356,15 @@ void LineEdit::drop_data(const Point2& p_point,const Variant& p_data){ if (p_data.get_type()==Variant::STRING) { set_cursor_at_pixel_pos(p_point.x); int selected = selection.end - selection.begin; + + Ref<Font> font = get_font("font"); + if (font != NULL) { + for (int i = selection.begin; i < selection.end; i++) + cached_width -= font->get_char_size(text[i]).width; + } + text.erase(selection.begin, selected); + append_at_cursor(p_data); selection.begin = cursor_pos-selected; selection.end = cursor_pos; @@ -365,8 +404,25 @@ void LineEdit::_notification(int p_what) { get_stylebox("focus")->draw( ci, Rect2( Point2(), size ) ); } - - int ofs=style->get_offset().x; + int x_ofs=0; + + switch (align) { + + case ALIGN_FILL: + case ALIGN_LEFT: { + + x_ofs=style->get_offset().x; + } break; + case ALIGN_CENTER: { + + x_ofs=x_ofs=int(size.width-(cached_width))/2; + } break; + case ALIGN_RIGHT: { + + x_ofs=x_ofs=int(size.width-style->get_offset().x-(cached_width)); + } break; + } + int ofs_max=width-style->get_minimum_size().width; int char_ofs=window_pos; @@ -391,29 +447,29 @@ void LineEdit::_notification(int p_what) { int char_width=font->get_char_size( cchar,next ).width; // end of widget, break! - if ( (ofs+char_width) > ofs_max ) + if ((x_ofs + char_width) > ofs_max) break; bool selected=selection.enabled && char_ofs>=selection.begin && char_ofs<selection.end; if (selected) - VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2( Point2( ofs , y_ofs ),Size2( char_width, y_area )),selection_color); + VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2(Point2(x_ofs, y_ofs), Size2(char_width, y_area)), selection_color); - font->draw_char(ci,Point2( ofs , y_ofs+font_ascent ), cchar, next,selected?font_color_selected:font_color ); + font->draw_char(ci, Point2(x_ofs, y_ofs + font_ascent), cchar, next, selected ? font_color_selected : font_color); if (char_ofs==cursor_pos && has_focus()) VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2( - Point2( ofs , y_ofs ), Size2( 1, y_area ) ), cursor_color ); + Point2( x_ofs , y_ofs ), Size2( 1, y_area ) ), cursor_color ); - ofs+=char_width; + x_ofs+=char_width; char_ofs++; } if (char_ofs==cursor_pos && has_focus()) //may be at the end VisualServer::get_singleton()->canvas_item_add_rect(ci, Rect2( - Point2( ofs , y_ofs ), Size2( 1, y_area ) ), cursor_color ); + Point2( x_ofs , y_ofs ), Size2( 1, y_area ) ), cursor_color ); } break; case NOTIFICATION_FOCUS_ENTER: { @@ -484,13 +540,36 @@ void LineEdit::shift_selection_check_post(bool p_shift) { void LineEdit::set_cursor_at_pixel_pos(int p_x) { - int ofs=window_pos; - int pixel_ofs=get_stylebox("normal")->get_offset().x; - Ref<Font> font=get_font("font"); + Ref<Font> font = get_font("font"); + int ofs = window_pos; + Ref<StyleBox> style = get_stylebox("normal"); + int pixel_ofs = 0; + Size2 size = get_size(); + + switch (align) { + + case ALIGN_FILL: + case ALIGN_LEFT: { + + pixel_ofs = int(style->get_offset().x); + } break; + case ALIGN_CENTER: { + + pixel_ofs=int(size.width-(cached_width))/2; + } break; + case ALIGN_RIGHT: { + + pixel_ofs=int(size.width-style->get_offset().x-(cached_width)); + } break; + } + while (ofs<text.length()) { - int char_w=font->get_char_size( text[ofs] ).width; + int char_w = 0; + if (font != NULL) { + int char_w = font->get_char_size(text[ofs]).width; + } pixel_ofs+=char_w; if (pixel_ofs > p_x) { //found what we look for @@ -523,6 +602,10 @@ void LineEdit::delete_char() { if ((text.length()<=0) || (cursor_pos==0)) return; + Ref<Font> font = get_font("font"); + if (font != NULL) { + cached_width -= font->get_char_size(text[cursor_pos - 1]).width; + } text.erase( cursor_pos-1, 1 ); @@ -593,13 +676,15 @@ void LineEdit::set_cursor_pos(int p_pos) { int width_to_cursor=0; int wp=window_pos; - for (int i=window_pos;i<cursor_pos;i++) - width_to_cursor+=font->get_char_size( text[i] ).width; + if (font != NULL) { + for (int i=window_pos;i<cursor_pos;i++) + width_to_cursor+=font->get_char_size( text[i] ).width; - while(width_to_cursor>=window_width && wp<text.length()) { - - width_to_cursor-=font->get_char_size( text[ wp ] ).width; - wp++; + while (width_to_cursor >= window_width && wp < text.length()) { + + width_to_cursor -= font->get_char_size(text[wp]).width; + wp++; + } } if (wp!=window_pos) @@ -626,17 +711,26 @@ void LineEdit::append_at_cursor(String p_text) { if ( ( max_length <= 0 ) || (text.length()+p_text.length() <= max_length)) { undo_text = text; + + Ref<Font> font = get_font("font"); + if (font != NULL) { + for (int i = 0; i < p_text.length(); i++) + cached_width += font->get_char_size(p_text[i]).width; + } + else { + cached_width = 0; + } + String pre = text.substr( 0, cursor_pos ); String post = text.substr( cursor_pos, text.length()-cursor_pos ); text=pre+p_text+post; set_cursor_pos(cursor_pos+p_text.length()); - } - } void LineEdit::clear_internal() { + cached_width = 0; cursor_pos=0; window_pos=0; undo_text=""; @@ -676,6 +770,20 @@ void LineEdit::selection_delete() { if (selection.enabled) { undo_text = text; + + if (text.size() > 0) + { + Ref<Font> font = get_font("font"); + if (font != NULL) { + for (int i = selection.begin; i < selection.end; i++) + cached_width -= font->get_char_size(text[i]).width; + } + } + else + { + cached_width = 0; + } + text.erase(selection.begin,selection.end-selection.begin); cursor_pos-=CLAMP( cursor_pos-selection.begin, 0, selection.end-selection.begin); @@ -782,9 +890,15 @@ void LineEdit::select(int p_from, int p_to) { update(); } +bool LineEdit::is_text_field() const { + + return true; +} void LineEdit::_bind_methods() { + ObjectTypeDB::bind_method(_MD("set_align", "align"), &LineEdit::set_align); + ObjectTypeDB::bind_method(_MD("get_align"), &LineEdit::get_align); ObjectTypeDB::bind_method(_MD("_input_event"),&LineEdit::_input_event); ObjectTypeDB::bind_method(_MD("clear"),&LineEdit::clear); @@ -805,15 +919,22 @@ void LineEdit::_bind_methods() { ADD_SIGNAL( MethodInfo("text_changed", PropertyInfo( Variant::STRING, "text" )) ); ADD_SIGNAL( MethodInfo("text_entered", PropertyInfo( Variant::STRING, "text" )) ); + BIND_CONSTANT(ALIGN_LEFT); + BIND_CONSTANT(ALIGN_CENTER); + BIND_CONSTANT(ALIGN_RIGHT); + BIND_CONSTANT(ALIGN_FILL); + ADD_PROPERTY( PropertyInfo( Variant::STRING, "text" ), _SCS("set_text"),_SCS("get_text") ); + ADD_PROPERTYNZ(PropertyInfo(Variant::INT, "align", PROPERTY_HINT_ENUM, "Left,Center,Right,Fill"), _SCS("set_align"), _SCS("get_align")); ADD_PROPERTY( PropertyInfo( Variant::INT, "max_length" ), _SCS("set_max_length"),_SCS("get_max_length") ); ADD_PROPERTY( PropertyInfo( Variant::BOOL, "editable" ), _SCS("set_editable"),_SCS("is_editable") ); ADD_PROPERTY( PropertyInfo( Variant::BOOL, "secret" ), _SCS("set_secret"),_SCS("is_secret") ); - } LineEdit::LineEdit() { + align = ALIGN_LEFT; + cached_width = 0; cursor_pos=0; window_pos=0; max_length = 0; diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 6c18594cda..f28136d66e 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -36,7 +36,18 @@ class LineEdit : public Control { OBJ_TYPE( LineEdit, Control ); - + +public: + enum Align { + + ALIGN_LEFT, + ALIGN_CENTER, + ALIGN_RIGHT, + ALIGN_FILL + }; +private: + Align align; + bool editable; bool pass; @@ -46,6 +57,8 @@ class LineEdit : public Control { int cursor_pos; int window_pos; int max_length; // 0 for no maximum + + int cached_width; struct Selection { @@ -83,7 +96,8 @@ class LineEdit : public Control { protected: static void _bind_methods(); public: - + void set_align(Align p_align); + Align get_align() const; virtual Variant get_drag_data(const Point2& p_point); virtual bool can_drop_data(const Point2& p_point,const Variant& p_data) const; @@ -112,9 +126,14 @@ public: void select(int p_from=0, int p_to=-1); virtual Size2 get_minimum_size() const; + + virtual bool is_text_field() const; LineEdit(); ~LineEdit(); }; + +VARIANT_ENUM_CAST(LineEdit::Align); + #endif diff --git a/scene/gui/margin_container.cpp b/scene/gui/margin_container.cpp index 54373b7592..f10ca6353a 100644 --- a/scene/gui/margin_container.cpp +++ b/scene/gui/margin_container.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/margin_container.h b/scene/gui/margin_container.h index 7f0a1f53e3..56f2344ea7 100644 --- a/scene/gui/margin_container.h +++ b/scene/gui/margin_container.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/menu_button.cpp b/scene/gui/menu_button.cpp index 7353744d07..be7a6b468a 100644 --- a/scene/gui/menu_button.cpp +++ b/scene/gui/menu_button.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -54,6 +54,8 @@ void MenuButton::_unhandled_key_input(InputEvent p_event) { int item = popup->find_item_by_accelerator(code); + + if (item>=0 && ! popup->is_item_disabled(item)) popup->activate_item(item); /* @@ -84,6 +86,7 @@ void MenuButton::pressed() { popup->set_parent_rect( Rect2(Point2(gp-popup->get_global_pos()),get_size())); popup->popup(); popup->call_deferred("grab_click_focus"); + popup->set_invalidate_click_until_motion(); } @@ -123,7 +126,7 @@ void MenuButton::_set_items(const Array& p_items) { void MenuButton::_bind_methods() { - ObjectTypeDB::bind_method(_MD("get_popup"),&MenuButton::get_popup); + ObjectTypeDB::bind_method(_MD("get_popup:PopupMenu"),&MenuButton::get_popup); ObjectTypeDB::bind_method(_MD("_unhandled_key_input"),&MenuButton::_unhandled_key_input); ObjectTypeDB::bind_method(_MD("_set_items"),&MenuButton::_set_items); ObjectTypeDB::bind_method(_MD("_get_items"),&MenuButton::_get_items); diff --git a/scene/gui/menu_button.h b/scene/gui/menu_button.h index 52c9a9aea5..47e7382d34 100644 --- a/scene/gui/menu_button.h +++ b/scene/gui/menu_button.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/option_button.cpp b/scene/gui/option_button.cpp index 78cc1bcdef..ff94a37be0 100644 --- a/scene/gui/option_button.cpp +++ b/scene/gui/option_button.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/option_button.h b/scene/gui/option_button.h index 295127175d..7d850479aa 100644 --- a/scene/gui/option_button.h +++ b/scene/gui/option_button.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/panel.cpp b/scene/gui/panel.cpp index fd1d5d2de6..d9ba20810b 100644 --- a/scene/gui/panel.cpp +++ b/scene/gui/panel.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/panel.h b/scene/gui/panel.h index 8dab05f1a6..7e6be62923 100644 --- a/scene/gui/panel.h +++ b/scene/gui/panel.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/panel_container.cpp b/scene/gui/panel_container.cpp index 91581ecccd..5ee061356e 100644 --- a/scene/gui/panel_container.cpp +++ b/scene/gui/panel_container.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/panel_container.h b/scene/gui/panel_container.h index ac67b52be8..c09479241c 100644 --- a/scene/gui/panel_container.h +++ b/scene/gui/panel_container.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/patch_9_frame.cpp b/scene/gui/patch_9_frame.cpp new file mode 100644 index 0000000000..b6e261714c --- /dev/null +++ b/scene/gui/patch_9_frame.cpp @@ -0,0 +1,132 @@ +#include "patch_9_frame.h" + +#include "servers/visual_server.h" + +void Patch9Frame::_notification(int p_what) { + + if (p_what==NOTIFICATION_DRAW) { + + if (texture.is_null()) + return; + + + Size2 s=get_size(); + RID ci = get_canvas_item(); + VS::get_singleton()->canvas_item_add_style_box(ci,Rect2(Point2(),s),texture->get_rid(),Vector2(margin[MARGIN_LEFT],margin[MARGIN_TOP]),Vector2(margin[MARGIN_RIGHT],margin[MARGIN_BOTTOM]),draw_center,modulate); +// draw_texture_rect(texture,Rect2(Point2(),s),false,modulate); + +/* + Vector<Point2> points; + points.resize(4); + points[0]=Point2(0,0); + points[1]=Point2(s.x,0); + points[2]=Point2(s.x,s.y); + points[3]=Point2(0,s.y); + Vector<Point2> uvs; + uvs.resize(4); + uvs[0]=Point2(0,0); + uvs[1]=Point2(1,0); + uvs[2]=Point2(1,1); + uvs[3]=Point2(0,1); + + VisualServer::get_singleton()->canvas_item_add_primitive(ci,points,Vector<Color>(),uvs,texture->get_rid()); +*/ + } +} + +Size2 Patch9Frame::get_minimum_size() const { + + return Size2(margin[MARGIN_LEFT]+margin[MARGIN_RIGHT],margin[MARGIN_TOP]+margin[MARGIN_BOTTOM]); +} +void Patch9Frame::_bind_methods() { + + + ObjectTypeDB::bind_method(_MD("set_texture","texture"), & Patch9Frame::set_texture ); + ObjectTypeDB::bind_method(_MD("get_texture"), & Patch9Frame::get_texture ); + ObjectTypeDB::bind_method(_MD("set_modulate","modulate"), & Patch9Frame::set_modulate ); + ObjectTypeDB::bind_method(_MD("get_modulate"), & Patch9Frame::get_modulate ); + ObjectTypeDB::bind_method(_MD("set_patch_margin","margin","value"), & Patch9Frame::set_patch_margin ); + ObjectTypeDB::bind_method(_MD("get_patch_margin","margin"), & Patch9Frame::get_patch_margin ); + ObjectTypeDB::bind_method(_MD("set_draw_center","draw_center"), & Patch9Frame::set_draw_center ); + ObjectTypeDB::bind_method(_MD("get_draw_center"), & Patch9Frame::get_draw_center ); + + ADD_PROPERTYNZ( PropertyInfo( Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), _SCS("set_texture"),_SCS("get_texture") ); + ADD_PROPERTYNO( PropertyInfo( Variant::COLOR, "modulate"), _SCS("set_modulate"),_SCS("get_modulate") ); + ADD_PROPERTYNO( PropertyInfo( Variant::BOOL, "draw_center"), _SCS("set_draw_center"),_SCS("get_draw_center") ); + ADD_PROPERTYINZ( PropertyInfo( Variant::INT, "patch_margin/left",PROPERTY_HINT_RANGE,"0,16384,1"), _SCS("set_patch_margin"),_SCS("get_patch_margin"),MARGIN_LEFT ); + ADD_PROPERTYINZ( PropertyInfo( Variant::INT, "patch_margin/top",PROPERTY_HINT_RANGE,"0,16384,1"), _SCS("set_patch_margin"),_SCS("get_patch_margin"),MARGIN_TOP ); + ADD_PROPERTYINZ( PropertyInfo( Variant::INT, "patch_margin/right",PROPERTY_HINT_RANGE,"0,16384,1"), _SCS("set_patch_margin"),_SCS("get_patch_margin"),MARGIN_RIGHT ); + ADD_PROPERTYINZ( PropertyInfo( Variant::INT, "patch_margin/bottom",PROPERTY_HINT_RANGE,"0,16384,1"), _SCS("set_patch_margin"),_SCS("get_patch_margin"),MARGIN_BOTTOM ); + +} + + +void Patch9Frame::set_texture(const Ref<Texture>& p_tex) { + + texture=p_tex; + update(); + //if (texture.is_valid()) + // texture->set_flags(texture->get_flags()&(~Texture::FLAG_REPEAT)); //remove repeat from texture, it looks bad in sprites + minimum_size_changed(); +} + +Ref<Texture> Patch9Frame::get_texture() const { + + return texture; +} + +void Patch9Frame::set_modulate(const Color& p_tex) { + + modulate=p_tex; + update(); +} + +Color Patch9Frame::get_modulate() const{ + + return modulate; +} + + +void Patch9Frame::set_patch_margin(Margin p_margin,int p_size) { + + ERR_FAIL_INDEX(p_margin,4); + margin[p_margin]=p_size; + update(); + minimum_size_changed(); +} + +int Patch9Frame::get_patch_margin(Margin p_margin) const{ + + ERR_FAIL_INDEX_V(p_margin,4,0); + return margin[p_margin]; +} + +void Patch9Frame::set_draw_center(bool p_draw) { + + draw_center=p_draw; + update(); +} + +bool Patch9Frame::get_draw_center() const{ + + return draw_center; +} + +Patch9Frame::Patch9Frame() { + + + margin[MARGIN_LEFT]=0; + margin[MARGIN_RIGHT]=0; + margin[MARGIN_BOTTOM]=0; + margin[MARGIN_TOP]=0; + modulate=Color(1,1,1,1); + set_ignore_mouse(true); + draw_center=true; +} + + +Patch9Frame::~Patch9Frame() +{ +} + + diff --git a/scene/gui/patch_9_frame.h b/scene/gui/patch_9_frame.h new file mode 100644 index 0000000000..562a5b1d77 --- /dev/null +++ b/scene/gui/patch_9_frame.h @@ -0,0 +1,40 @@ +#ifndef PATCH_9_FRAME_H +#define PATCH_9_FRAME_H + +#include "scene/gui/control.h" +/** + @author Juan Linietsky <reduzio@gmail.com> +*/ +class Patch9Frame : public Control { + + OBJ_TYPE(Patch9Frame,Control); + + bool draw_center; + int margin[4]; + Color modulate; + Ref<Texture> texture; +protected: + + void _notification(int p_what); + virtual Size2 get_minimum_size() const; + static void _bind_methods(); + +public: + + void set_texture(const Ref<Texture>& p_tex); + Ref<Texture> get_texture() const; + + void set_modulate(const Color& p_tex); + Color get_modulate() const; + + void set_patch_margin(Margin p_margin,int p_size); + int get_patch_margin(Margin p_margin) const; + + void set_draw_center(bool p_enable); + bool get_draw_center() const; + + Patch9Frame(); + ~Patch9Frame(); + +}; +#endif // PATCH_9_FRAME_H diff --git a/scene/gui/popup.cpp b/scene/gui/popup.cpp index 65ad02723c..5ce7e2e0d3 100644 --- a/scene/gui/popup.cpp +++ b/scene/gui/popup.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -51,11 +51,18 @@ void Popup::_fix_size() { Control *window = get_window(); ERR_FAIL_COND(!window); - + +#if 0 Point2 pos = get_pos(); Size2 size = get_size(); Point2 window_size = window==this ? get_parent_area_size() :window->get_size(); +#else + + Point2 pos = get_global_pos(); + Size2 size = get_size(); + Point2 window_size = get_viewport_rect().size; +#endif if (pos.x+size.width > window_size.width) pos.x=window_size.width-size.width; if (pos.x<0) @@ -65,8 +72,56 @@ void Popup::_fix_size() { pos.y=window_size.height-size.height; if (pos.y<0) pos.y=0; +#if 0 if (pos!=get_pos()) set_pos(pos); +#else + if (pos!=get_pos()) + set_global_pos(pos); + +#endif + +} + + +void Popup::set_as_minsize() { + + Size2 total_minsize; + + for(int i=0;i<get_child_count();i++) { + + Control *c=get_child(i)->cast_to<Control>(); + if (!c) + continue; + if (c->is_hidden()) + continue; + + Size2 minsize = c->get_combined_minimum_size(); + + for(int j=0;j<2;j++) { + + Margin m_beg = Margin(0+j); + Margin m_end = Margin(2+j); + + float margin_begin = c->get_margin(m_beg); + float margin_end = c->get_margin(m_end); + AnchorType anchor_begin = c->get_anchor(m_beg); + AnchorType anchor_end = c->get_anchor(m_end); + + if (anchor_begin == ANCHOR_BEGIN) + minsize[j]+=margin_begin; + if (anchor_end == ANCHOR_END) + minsize[j]+=margin_end; + + } + + print_line(String(c->get_type())+": "+minsize); + + total_minsize.width = MAX( total_minsize.width, minsize.width ); + total_minsize.height = MAX( total_minsize.height, minsize.height ); + } + + set_size(total_minsize); } @@ -81,6 +136,8 @@ void Popup::popup_centered_minsize(const Size2& p_minsize) { Control *c=get_child(i)->cast_to<Control>(); if (!c) continue; + if (c->is_hidden()) + continue; Size2 minsize = c->get_combined_minimum_size(); @@ -101,6 +158,8 @@ void Popup::popup_centered_minsize(const Size2& p_minsize) { } + print_line(String(c->get_type())+": "+minsize); + total_minsize.width = MAX( total_minsize.width, minsize.width ); total_minsize.height = MAX( total_minsize.height, minsize.height ); } diff --git a/scene/gui/popup.h b/scene/gui/popup.h index 072b66c2c7..6c72a3c82b 100644 --- a/scene/gui/popup.h +++ b/scene/gui/popup.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -62,6 +62,7 @@ public: void popup_centered_ratio(float p_screen_ratio=0.75); void popup_centered(const Size2& p_size=Size2()); void popup_centered_minsize(const Size2& p_minsize=Size2()); + void set_as_minsize(); virtual void popup(); diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 0ba3bdb7c6..99663fb2e2 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -30,6 +30,7 @@ #include "print_string.h" #include "os/keyboard.h" #include "translation.h" +#include "os/input.h" String PopupMenu::_get_accel_text(uint32_t p_accel) const { @@ -318,8 +319,17 @@ void PopupMenu::_input_event(const InputEvent &p_event) { int over=_get_mouse_over(Point2(b.x,b.y)); - if (over<0 || items[over].separator || items[over].disabled) + if (invalidated_click) { + invalidated_click=false; + break; + } + if (over<0) { + hide(); break; //non-activable + } + + if (items[over].separator || items[over].disabled) + break; if (items[over].submenu!="") { @@ -336,6 +346,13 @@ void PopupMenu::_input_event(const InputEvent &p_event) { case InputEvent::MOUSE_MOTION: { + if (invalidated_click) { + moved+=Vector2(p_event.mouse_motion.relative_x,p_event.mouse_motion.relative_y); + if (moved.length()>4) + invalidated_click=false; + + } + const InputEventMouseMotion &m=p_event.mouse_motion; for(List<Rect2>::Element *E=autohide_areas.front();E;E=E->next()) { @@ -348,8 +365,11 @@ void PopupMenu::_input_event(const InputEvent &p_event) { int over=_get_mouse_over(Point2(m.x,m.y)); int id = (over<0 || items[over].separator || items[over].disabled)?-1:items[over].ID; - if (id<0) + if (id<0) { + mouse_over=-1; + update(); break; + } if (items[over].submenu!="" && submenu_over!=over) { submenu_over=over; @@ -726,10 +746,18 @@ int PopupMenu::find_item_by_accelerator(uint32_t p_accel) const { void PopupMenu::activate_item(int p_item) { - ERR_FAIL_INDEX(p_item,items.size()); ERR_FAIL_COND(items[p_item].separator); emit_signal("item_pressed",items[p_item].ID); + + //hide all parent PopupMenue's + Node *next = get_parent(); + PopupMenu *pop = next->cast_to<PopupMenu>(); + while (pop) { + pop->hide(); + next = next->get_parent(); + pop = next->cast_to<PopupMenu>(); + } hide(); } @@ -752,6 +780,7 @@ void PopupMenu::add_separator() { void PopupMenu::clear() { items.clear(); + mouse_over=-1; update(); idcount=0; @@ -893,12 +922,17 @@ void PopupMenu::_bind_methods() { } + +void PopupMenu::set_invalidate_click_until_motion() { + moved=Vector2(); + invalidated_click=true; +} + PopupMenu::PopupMenu() { idcount=0; mouse_over=-1; - set_focus_mode(FOCUS_ALL); set_as_toplevel(true); diff --git a/scene/gui/popup_menu.h b/scene/gui/popup_menu.h index b150be1008..ed78fe6738 100644 --- a/scene/gui/popup_menu.h +++ b/scene/gui/popup_menu.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -70,6 +70,8 @@ class PopupMenu : public Popup { void _activate_submenu(int over); void _submenu_timeout(); + bool invalidated_click; + Vector2 moved; Array _get_items() const; void _set_items(const Array& p_items); @@ -88,7 +90,7 @@ public: void add_icon_check_item(const Ref<Texture>& p_icon,const String& p_label,int p_ID=-1,uint32_t p_accel=0); void add_check_item(const String& p_label,int p_ID=-1,uint32_t p_accel=0); void add_submenu_item(const String& p_label,const String& p_submenu, int p_ID=-1); - + void set_item_text(int p_idx,const String& p_text); void set_item_icon(int p_idx,const Ref<Texture>& p_icon); void set_item_checked(int p_idx,bool p_checked); @@ -134,6 +136,8 @@ public: void add_autohide_area(const Rect2& p_area); void clear_autohide_areas(); + void set_invalidate_click_until_motion(); + PopupMenu(); ~PopupMenu(); diff --git a/scene/gui/progress_bar.cpp b/scene/gui/progress_bar.cpp index 73fa1fbb98..e7e2c88f4a 100644 --- a/scene/gui/progress_bar.cpp +++ b/scene/gui/progress_bar.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/progress_bar.h b/scene/gui/progress_bar.h index fd34c67fae..33b0d5c185 100644 --- a/scene/gui/progress_bar.h +++ b/scene/gui/progress_bar.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/range.cpp b/scene/gui/range.cpp index 4d0b678925..7103ee651f 100644 --- a/scene/gui/range.cpp +++ b/scene/gui/range.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -243,7 +243,7 @@ void Range::_bind_methods() { ADD_PROPERTY( PropertyInfo( Variant::REAL, "range/step" ), _SCS("set_step"), _SCS("get_step") ); ADD_PROPERTY( PropertyInfo( Variant::REAL, "range/page" ), _SCS("set_page"), _SCS("get_page") ); ADD_PROPERTY( PropertyInfo( Variant::REAL, "range/value" ), _SCS("set_val"), _SCS("get_val") ); - ADD_PROPERTY( PropertyInfo( Variant::REAL, "range/exp_edit" ), _SCS("set_exp_unit_value"), _SCS("is_unit_value_exp") ); + ADD_PROPERTY( PropertyInfo( Variant::BOOL, "range/exp_edit" ), _SCS("set_exp_unit_value"), _SCS("is_unit_value_exp") ); ADD_PROPERTY( PropertyInfo( Variant::BOOL, "rounded_values" ), _SCS("set_rounded_values"), _SCS("get_rounded_values") ); } diff --git a/scene/gui/range.h b/scene/gui/range.h index e33a71e6ab..48361ddb0e 100644 --- a/scene/gui/range.h +++ b/scene/gui/range.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/reference_frame.cpp b/scene/gui/reference_frame.cpp index 44ba3a8972..b90ea8292d 100644 --- a/scene/gui/reference_frame.cpp +++ b/scene/gui/reference_frame.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/reference_frame.h b/scene/gui/reference_frame.h index 954b17b5c8..8915b1df0c 100644 --- a/scene/gui/reference_frame.h +++ b/scene/gui/reference_frame.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/rich_text_label.cpp b/scene/gui/rich_text_label.cpp index 94df42fc8f..b98fec1bde 100644 --- a/scene/gui/rich_text_label.cpp +++ b/scene/gui/rich_text_label.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -58,7 +58,7 @@ RichTextLabel::Item *RichTextLabel::_get_next_item(Item* p_item) { } -void RichTextLabel::_process_line(int &y, int p_width, int p_line, ProcessMode p_mode,const Ref<Font> &p_base_font,const Color &p_base_color,const Point2i& p_click_pos,Item **r_click_item,int *r_click_char,bool *r_outside) { +void RichTextLabel::_process_line(int &y, int p_width, int p_line, ProcessMode p_mode,const Ref<Font> &p_base_font,const Color &p_base_color,const Point2i& p_click_pos,Item **r_click_item,int *r_click_char,bool *r_outside,int p_char_count) { RID ci; if (r_outside) @@ -78,6 +78,8 @@ void RichTextLabel::_process_line(int &y, int p_width, int p_line, ProcessMode p int margin=_find_margin(it,p_base_font); Align align=_find_align(it);; int line=0; + int spaces=0; + if (p_mode!=PROCESS_CACHE) { @@ -85,7 +87,14 @@ void RichTextLabel::_process_line(int &y, int p_width, int p_line, ProcessMode p line_ofs = l.offset_caches[line]; } + if (p_mode==PROCESS_CACHE) { + l.offset_caches.clear(); + l.height_caches.clear(); + l.char_count=0; + } + int wofs=margin; + int spaces_size=0; if (p_mode!=PROCESS_CACHE && align!=ALIGN_FILL) wofs+=line_ofs; @@ -101,7 +110,6 @@ void RichTextLabel::_process_line(int &y, int p_width, int p_line, ProcessMode p Variant meta; - #define NEW_LINE \ {\ if (p_mode!=PROCESS_CACHE) {\ @@ -117,12 +125,15 @@ void RichTextLabel::_process_line(int &y, int p_width, int p_line, ProcessMode p case ALIGN_LEFT: l.offset_caches.push_back(0); break;\ case ALIGN_CENTER: l.offset_caches.push_back(((p_width-margin)-used)/2); break;\ case ALIGN_RIGHT: l.offset_caches.push_back(((p_width-margin)-used)); break;\ - case ALIGN_FILL: l.offset_caches.push_back(p_width-wofs); break;\ + case ALIGN_FILL: l.offset_caches.push_back((p_width-margin)-used+spaces_size); break;\ }\ l.height_caches.push_back(line_height);\ + l.space_caches.push_back(spaces);\ }\ y+=line_height+get_constant(SceneStringNames::get_singleton()->line_separation);\ line_height=0;\ + spaces=0;\ + spaces_size=0;\ wofs=begin;\ if (p_mode!=PROCESS_CACHE) {\ lh=line<l.height_caches.size()?l.height_caches[line]:1;\ @@ -138,6 +149,10 @@ void RichTextLabel::_process_line(int &y, int p_width, int p_line, ProcessMode p #define ENSURE_WIDTH(m_width) \ if (wofs + m_width > p_width) {\ + if (p_mode==PROCESS_CACHE) {\ + if (spaces>0) \ + spaces-=1;\ + }\ if (p_mode==PROCESS_POINTER && r_click_item && p_click_pos.y>=y && p_click_pos.y<=y+lh && p_click_pos.x>wofs) {\ if (r_outside) *r_outside=true; \ *r_click_item=it;\ @@ -203,33 +218,76 @@ if (m_height > line_height) {\ underline=true; } + } else if (p_mode==PROCESS_CACHE) { + l.char_count+=text->text.length(); } rchar=0; + while(*c) { int end=0; int w=0; + int fw=0; lh=0; if (p_mode!=PROCESS_CACHE) { lh=line<l.height_caches.size()?l.height_caches[line]:1; } + bool found_space=false; while (c[end]!=0 && !(end && c[end-1]==' ' && c[end]!=' ')) { + int cw = font->get_char_size(c[end],c[end+1]).width; + if (c[end]=='\t') { + cw=tab_size*font->get_char_size(' ').width; + } w+=cw; - end++; + + if (c[end]==' ') { + + if (p_mode==PROCESS_CACHE) { + fw+=cw; + } else if (align==ALIGN_FILL && line<l.space_caches.size() && l.space_caches[line]>0) { + //print_line(String(c,end)+": "+itos(l.offset_caches[line])+"/"+itos(l.space_caches[line])); + //sub_space=cw; + found_space=true; + } else { + fw+=cw; + } + } else { + fw+=cw; + } + + end++; } - ENSURE_WIDTH(w); + ENSURE_WIDTH(w); + + + //print_line("END: "+String::chr(c[end])+"."); + if (end && c[end-1]==' ') { + spaces++; + if (p_mode==PROCESS_CACHE) { + spaces_size+=font->get_char_size(' ').width; + } + + if (found_space) { + int ln = MIN(l.offset_caches.size()-1,line); + + fw+=l.offset_caches[ln]/l.space_caches[ln]; + } + + } + { int ofs=0; + for(int i=0;i<end;i++) { int pofs=wofs+ofs; @@ -239,7 +297,11 @@ if (m_height > line_height) {\ if (p_mode==PROCESS_POINTER && r_click_char && p_click_pos.y>=y && p_click_pos.y<=y+lh) { //int o = (wofs+w)-p_click_pos.x; + int cw=font->get_char_size(c[i],c[i+1]).x; + if (c[i]=='\t') { + cw=tab_size*font->get_char_size(' ').width; + } if (p_click_pos.x-cw/2>pofs) { @@ -268,22 +330,36 @@ if (m_height > line_height) {\ } } - int cw; + int cw=0; + + bool visible = visible_characters<0 || p_char_count<visible_characters; if (selected) { cw = font->get_char_size(c[i],c[i+1]).x; draw_rect(Rect2(pofs,y,cw,lh),selection_bg); - font->draw_char(ci,Point2(pofs,y+lh-(fh-ascent)),c[i],c[i+1],selection_fg); + if (visible) + font->draw_char(ci,Point2(pofs,y+lh-(fh-ascent)),c[i],c[i+1],selection_fg); } else { - cw=font->draw_char(ci,Point2(pofs,y+lh-(fh-ascent)),c[i],c[i+1],color); + if (visible) + cw=font->draw_char(ci,Point2(pofs,y+lh-(fh-ascent)),c[i],c[i+1],color); } + p_char_count++; + if (c[i]=='\t') { + cw=tab_size*font->get_char_size(' ').width; + } + + + //print_line("draw char: "+String::chr(c[i])); + if (underline) { Color uc=color; - uc.a*=0.3; - VS::get_singleton()->canvas_item_add_line(ci,Point2(pofs,y+ascent+2),Point2(pofs+cw,y+ascent+2),uc); + uc.a*=0.5; + //VS::get_singleton()->canvas_item_add_line(ci,Point2(pofs,y+ascent+2),Point2(pofs+cw,y+ascent+2),uc); + int uy = y+lh-fh+ascent+2; + VS::get_singleton()->canvas_item_add_line(ci,Point2(pofs,uy),Point2(pofs+cw,uy),uc); } ofs+=cw; } @@ -292,7 +368,7 @@ if (m_height > line_height) {\ } - ADVANCE(w); + ADVANCE(fw); CHECK_HEIGHT(fh); //must be done somewhere c=&c[end]; } @@ -304,6 +380,8 @@ if (m_height > line_height) {\ lh=0; if (p_mode!=PROCESS_CACHE) lh = line<l.height_caches.size()?l.height_caches[line]:1; + else + l.char_count+=1; //images count as chars too ItemImage *img = static_cast<ItemImage*>(it); @@ -316,9 +394,12 @@ if (m_height > line_height) {\ ENSURE_WIDTH( img->image->get_width() ); - if (p_mode==PROCESS_DRAW) { + bool visible = visible_characters<0 || p_char_count<visible_characters; + + if (p_mode==PROCESS_DRAW && visible) { img->image->draw(ci,Point2(wofs,y+lh-font->get_descent()-img->image->get_height())); } + p_char_count++; ADVANCE( img->image->get_width() ); CHECK_HEIGHT( (img->image->get_height()+font->get_descent()) ); @@ -450,6 +531,23 @@ void RichTextLabel::_notification(int p_what) { update(); } break; + case NOTIFICATION_ENTER_TREE: { + + if (use_bbcode) + parse_bbcode(bbcode); + first_invalid_line=0; //invalidate ALL + update(); + + } break; + case NOTIFICATION_THEME_CHANGED: { + + if (is_inside_tree() && use_bbcode) { + parse_bbcode(bbcode); + //first_invalid_line=0; //invalidate ALL + //update(); + } + + } break; case NOTIFICATION_DRAW: { _validate_line_caches(); @@ -472,23 +570,26 @@ void RichTextLabel::_notification(int p_what) { //todo, change to binary search int from_line = 0; + int total_chars = 0; while (from_line<lines.size()) { if (lines[from_line].height_accum_cache>=ofs) break; from_line++; + total_chars+=lines[from_line].char_count; } if (from_line>=lines.size()) break; //nothing to draw int y = (lines[from_line].height_accum_cache - lines[from_line].height_cache) - ofs; - Ref<Font> base_font=get_font("default_font"); + Ref<Font> base_font=get_font("normal_font"); Color base_color=get_color("default_color"); while (y<size.height && from_line<lines.size()) { - _process_line(y,size.width-scroll_w,from_line,PROCESS_DRAW,base_font,base_color); + _process_line(y,size.width-scroll_w,from_line,PROCESS_DRAW,base_font,base_color,Point2i(),NULL,NULL,NULL,total_chars); + total_chars+=lines[from_line].char_count; from_line++; } } @@ -521,7 +622,7 @@ void RichTextLabel::_find_click(const Point2i& p_click,Item **r_click_item,int * int y = (lines[from_line].height_accum_cache - lines[from_line].height_cache) - ofs; - Ref<Font> base_font=get_font("default_font"); + Ref<Font> base_font=get_font("normal_font"); Color base_color=get_color("default_color"); @@ -618,7 +719,7 @@ void RichTextLabel::_input_event(InputEvent p_event) { case InputEvent::KEY: { const InputEventKey &k=p_event.key; - if (k.pressed) { + if (k.pressed && !k.mod.alt && !k.mod.shift && !k.mod.command && !k.mod.meta) { bool handled=true; switch(k.scancode) { case KEY_PAGEUP: { @@ -634,12 +735,12 @@ void RichTextLabel::_input_event(InputEvent p_event) { case KEY_UP: { if (vscroll->is_visible()) - vscroll->set_val( vscroll->get_val() - get_font("default_font")->get_height() ); + vscroll->set_val( vscroll->get_val() - get_font("normal_font")->get_height() ); } break; case KEY_DOWN: { if (vscroll->is_visible()) - vscroll->set_val( vscroll->get_val() + get_font("default_font")->get_height() ); + vscroll->set_val( vscroll->get_val() + get_font("normal_font")->get_height() ); } break; case KEY_HOME: { @@ -664,6 +765,7 @@ void RichTextLabel::_input_event(InputEvent p_event) { default: handled=false; } + if (handled) accept_event(); } @@ -856,13 +958,12 @@ void RichTextLabel::_validate_line_caches() { //validate invalid lines!s Size2 size = get_size(); - Ref<Font> base_font=get_font("default_font"); + Ref<Font> base_font=get_font("normal_font"); for(int i=first_invalid_line;i<lines.size();i++) { int y=0; _process_line(y,size.width-scroll_w,i,PROCESS_CACHE,base_font,Color()); - lines[i].height_cache=y; lines[i].height_accum_cache=y; @@ -1152,9 +1253,19 @@ Error RichTextLabel::append_bbcode(const String& p_bbcode) { int pos = 0; List<String> tag_stack; - Ref<Font> base_font=get_font("default_font"); + Ref<Font> normal_font=get_font("normal_font"); + Ref<Font> bold_font=get_font("bold_font"); + Ref<Font> italics_font=get_font("italics_font"); + Ref<Font> bold_italics_font=get_font("bold_italics_font"); + Ref<Font> mono_font=get_font("mono_font"); + Color base_color=get_color("default_color"); + int indent_level=0; + + bool in_bold=false; + bool in_italics=false; + while(pos < p_bbcode.length()) { @@ -1182,12 +1293,18 @@ Error RichTextLabel::append_bbcode(const String& p_bbcode) { String tag = p_bbcode.substr(brk_pos+1,brk_end-brk_pos-1); - if (tag.begins_with("/")) { + if (tag.begins_with("/") && tag_stack.size()) { bool tag_ok = tag_stack.size() && tag_stack.front()->get()==tag.substr(1,tag.length()); + if (tag_stack.front()->get()=="b") + in_bold=false; + if (tag_stack.front()->get()=="i") + in_italics=false; + if (tag_stack.front()->get()=="indent") + indent_level--; if (!tag_ok) { @@ -1205,19 +1322,27 @@ Error RichTextLabel::append_bbcode(const String& p_bbcode) { } else if (tag=="b") { //use bold font - push_font(base_font); + in_bold=true; + if (in_italics) + push_font(bold_italics_font); + else + push_font(bold_font); pos=brk_end+1; tag_stack.push_front(tag); } else if (tag=="i") { //use italics font - push_font(base_font); + in_italics=true; + if (in_bold) + push_font(bold_italics_font); + else + push_font(italics_font); pos=brk_end+1; tag_stack.push_front(tag); } else if (tag=="code") { //use monospace font - push_font(base_font); + push_font(mono_font); pos=brk_end+1; tag_stack.push_front(tag); } else if (tag=="u") { @@ -1232,6 +1357,43 @@ Error RichTextLabel::append_bbcode(const String& p_bbcode) { push_underline(); pos=brk_end+1; tag_stack.push_front(tag); + } else if (tag=="center") { + + //use underline + push_align(ALIGN_CENTER); + pos=brk_end+1; + tag_stack.push_front(tag); + } else if (tag=="fill") { + + //use underline + push_align(ALIGN_FILL); + pos=brk_end+1; + tag_stack.push_front(tag); + } else if (tag=="right") { + + //use underline + push_align(ALIGN_RIGHT); + pos=brk_end+1; + tag_stack.push_front(tag); + } else if (tag=="ul") { + + //use underline + push_list(LIST_DOTS); + pos=brk_end+1; + tag_stack.push_front(tag); + } else if (tag=="ol") { + + //use underline + push_list(LIST_NUMBERS); + pos=brk_end+1; + tag_stack.push_front(tag); + } else if (tag=="indent") { + + //use underline + indent_level++; + push_indent(indent_level); + pos=brk_end+1; + tag_stack.push_front(tag); } else if (tag=="url") { @@ -1244,6 +1406,7 @@ Error RichTextLabel::append_bbcode(const String& p_bbcode) { pos=brk_end+1; tag_stack.push_front(tag); + } else if (tag.begins_with("url=")) { String url = tag.substr(4,tag.length()); @@ -1321,7 +1484,7 @@ Error RichTextLabel::append_bbcode(const String& p_bbcode) { if (font.is_valid()) push_font(font); else - push_font(base_font); + push_font(normal_font); pos=brk_end+1; tag_stack.push_front("font"); @@ -1341,10 +1504,10 @@ Error RichTextLabel::append_bbcode(const String& p_bbcode) { void RichTextLabel::scroll_to_line(int p_line) { + p_line -= 1; ERR_FAIL_INDEX(p_line,lines.size()); _validate_line_caches(); - vscroll->set_val(lines[p_line].height_accum_cache); - + vscroll->set_val(lines[p_line].height_accum_cache-lines[p_line].height_cache); } @@ -1407,27 +1570,23 @@ bool RichTextLabel::search(const String& p_string,bool p_from_selection) { it=_get_next_item(it); } - if (!it) - line=lines.size()-1; } - scroll_to_line(line-2); + if (line > 1) { + line-=1; + } + + scroll_to_line(line); return true; } - } else if (it->type==ITEM_NEWLINE) { - - line=static_cast<ItemNewline*>(it)->line; } - it=_get_next_item(it); charidx=0; } - - return false; } @@ -1467,7 +1626,7 @@ void RichTextLabel::selection_copy() { if (text!="") { OS::get_singleton()->set_clipboard(text); - print_line("COPY: "+text); + //print_line("COPY: "+text); } } @@ -1477,7 +1636,29 @@ bool RichTextLabel::is_selection_enabled() const { return selection.enabled; } +void RichTextLabel::set_bbcode(const String& p_bbcode) { + bbcode=p_bbcode; + if (is_inside_tree() && use_bbcode) + parse_bbcode(p_bbcode); +} + +String RichTextLabel::get_bbcode() const { + + return bbcode; +} + +void RichTextLabel::set_use_bbcode(bool p_enable) { + if (use_bbcode==p_enable) + return; + use_bbcode=p_enable; + if (is_inside_tree() && use_bbcode) + parse_bbcode(bbcode); +} + +bool RichTextLabel::is_using_bbcode() const { + return use_bbcode; +} void RichTextLabel::_bind_methods() { @@ -1506,15 +1687,32 @@ void RichTextLabel::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_scroll_follow","follow"),&RichTextLabel::set_scroll_follow); ObjectTypeDB::bind_method(_MD("is_scroll_following"),&RichTextLabel::is_scroll_following); + ObjectTypeDB::bind_method(_MD("get_v_scroll"),&RichTextLabel::get_v_scroll); + ObjectTypeDB::bind_method(_MD("set_tab_size","spaces"),&RichTextLabel::set_tab_size); ObjectTypeDB::bind_method(_MD("get_tab_size"),&RichTextLabel::get_tab_size); + ObjectTypeDB::bind_method(_MD("set_selection_enabled","enabled"),&RichTextLabel::set_selection_enabled); ObjectTypeDB::bind_method(_MD("is_selection_enabled"),&RichTextLabel::is_selection_enabled); ObjectTypeDB::bind_method(_MD("parse_bbcode", "bbcode"),&RichTextLabel::parse_bbcode); ObjectTypeDB::bind_method(_MD("append_bbcode", "bbcode"),&RichTextLabel::append_bbcode); + ObjectTypeDB::bind_method(_MD("set_bbcode","text"),&RichTextLabel::set_bbcode); + ObjectTypeDB::bind_method(_MD("get_bbcode"),&RichTextLabel::get_bbcode); + + ObjectTypeDB::bind_method(_MD("set_visible_characters","amount"),&RichTextLabel::set_visible_characters); + ObjectTypeDB::bind_method(_MD("get_visible_characters"),&RichTextLabel::get_visible_characters); + + ObjectTypeDB::bind_method(_MD("get_total_character_count"),&RichTextLabel::get_total_character_count); + + ObjectTypeDB::bind_method(_MD("set_use_bbcode","enable"),&RichTextLabel::set_use_bbcode); + ObjectTypeDB::bind_method(_MD("is_using_bbcode"),&RichTextLabel::is_using_bbcode); + + ADD_PROPERTY(PropertyInfo(Variant::BOOL,"bbcode/enabled"),_SCS("set_use_bbcode"),_SCS("is_using_bbcode")); + ADD_PROPERTY(PropertyInfo(Variant::STRING,"bbcode/bbcode",PROPERTY_HINT_MULTILINE_TEXT),_SCS("set_bbcode"),_SCS("get_bbcode")); + ADD_PROPERTY(PropertyInfo(Variant::INT,"visible_characters",PROPERTY_HINT_RANGE,"-1,128000,1"),_SCS("set_visible_characters"),_SCS("get_visible_characters")); ADD_SIGNAL( MethodInfo("meta_clicked",PropertyInfo(Variant::NIL,"meta"))); @@ -1541,6 +1739,27 @@ void RichTextLabel::_bind_methods() { } + +void RichTextLabel::set_visible_characters(int p_visible) { + + visible_characters=p_visible; + update(); +} + +int RichTextLabel::get_visible_characters() const { + + return visible_characters; +} +int RichTextLabel::get_total_character_count() const { + + int tc=0; + for(int i=0;i<lines.size();i++) + tc+=lines[i].char_count; + + return tc; +} + + RichTextLabel::RichTextLabel() { @@ -1572,11 +1791,14 @@ RichTextLabel::RichTextLabel() { vscroll->set_step(1); vscroll->hide(); current_idx=1; + use_bbcode=false; selection.click=NULL; selection.active=false; selection.enabled=false; + visible_characters=-1; + } RichTextLabel::~RichTextLabel() { diff --git a/scene/gui/rich_text_label.h b/scene/gui/rich_text_label.h index 228d607049..eaa8d5d60a 100644 --- a/scene/gui/rich_text_label.h +++ b/scene/gui/rich_text_label.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -168,10 +168,12 @@ private: Item *from; Vector<int> offset_caches; Vector<int> height_caches; + Vector<int> space_caches; int height_cache; int height_accum_cache; + int char_count; - Line() { from=NULL; } + Line() { from=NULL; char_count=0; } }; @@ -222,10 +224,10 @@ private: Selection selection; + int visible_characters; - - void _process_line(int &y, int p_width, int p_line, ProcessMode p_mode,const Ref<Font> &p_base_font,const Color &p_base_color,const Point2i& p_click_pos=Point2i(),Item **r_click_item=NULL,int *r_click_char=NULL,bool *r_outside=NULL); + void _process_line(int &y, int p_width, int p_line, ProcessMode p_mode,const Ref<Font> &p_base_font,const Color &p_base_color,const Point2i& p_click_pos=Point2i(),Item **r_click_item=NULL,int *r_click_char=NULL,bool *r_outside=NULL,int p_char_count=0); void _find_click(const Point2i& p_click,Item **r_click_item=NULL,int *r_click_char=NULL,bool *r_outside=NULL); @@ -242,6 +244,10 @@ private: void _input_event(InputEvent p_event); Item *_get_next_item(Item* p_item); + bool use_bbcode; + String bbcode; + + protected: void _notification(int p_what); @@ -291,9 +297,20 @@ public: bool is_selection_enabled() const; void selection_copy(); + Error parse_bbcode(const String& p_bbcode); Error append_bbcode(const String& p_bbcode); + void set_use_bbcode(bool p_enable); + bool is_using_bbcode() const; + + void set_bbcode(const String& p_bbcode); + String get_bbcode() const; + + void set_visible_characters(int p_visible); + int get_visible_characters() const; + int get_total_character_count() const; + RichTextLabel(); ~RichTextLabel(); }; diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index aaf96114c6..b1fd914fcd 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -51,20 +51,20 @@ void ScrollBar::_input_event(InputEvent p_event) { if (b.button_index==5 && b.pressed) { - if (orientation==VERTICAL) - set_val( get_val() + get_page() / 4.0 ); - else - set_val( get_val() + get_page() / 4.0 ); + //if (orientation==VERTICAL) + // set_val( get_val() + get_page() / 4.0 ); + //else + set_val( get_val() + get_page() / 4.0 ); accept_event(); } if (b.button_index==4 && b.pressed) { - if (orientation==HORIZONTAL) - set_val( get_val() - get_page() / 4.0 ); - else - set_val( get_val() - get_page() / 4.0 ); + //if (orientation==HORIZONTAL) + // set_val( get_val() - get_page() / 4.0 ); + //else + set_val( get_val() - get_page() / 4.0 ); accept_event(); } diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h index ad3d1a7f58..367bc3eb53 100644 --- a/scene/gui/scroll_bar.h +++ b/scene/gui/scroll_bar.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/scroll_container.cpp b/scene/gui/scroll_container.cpp index 95354df519..e5b5d531a0 100644 --- a/scene/gui/scroll_container.cpp +++ b/scene/gui/scroll_container.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -195,11 +195,19 @@ void ScrollContainer::_notification(int p_what) { Rect2 r = Rect2(-scroll,minsize); if (!scroll_h) { r.pos.x=0; - r.size.width=size.width; + if (c->get_h_size_flags()&SIZE_EXPAND) + r.size.width=MAX(size.width,minsize.width); + else + r.size.width=minsize.width; } if (!scroll_v) { r.pos.y=0; r.size.height=size.height; + if (c->get_v_size_flags()&SIZE_EXPAND) + r.size.height=MAX(size.height,minsize.height); + else + r.size.height=minsize.height; + } fit_child_in_rect(c,r); } diff --git a/scene/gui/scroll_container.h b/scene/gui/scroll_container.h index c8b03b3671..b8d37be08c 100644 --- a/scene/gui/scroll_container.h +++ b/scene/gui/scroll_container.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/separator.cpp b/scene/gui/separator.cpp index aac9ac0d03..5e822a10ad 100644 --- a/scene/gui/separator.cpp +++ b/scene/gui/separator.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/separator.h b/scene/gui/separator.h index 177e69cffe..17e9c11e34 100644 --- a/scene/gui/separator.h +++ b/scene/gui/separator.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/slider.cpp b/scene/gui/slider.cpp index 39d0ccfd10..b6292c544b 100644 --- a/scene/gui/slider.cpp +++ b/scene/gui/slider.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/slider.h b/scene/gui/slider.h index d9cb7e754b..5850c48ce3 100644 --- a/scene/gui/slider.h +++ b/scene/gui/slider.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 9ac67e92f5..a48136f541 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -27,7 +27,7 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ #include "spin_box.h" - +#include "os/input.h" Size2 SpinBox::get_minimum_size() const { @@ -62,6 +62,13 @@ LineEdit *SpinBox::get_line_edit() { } +void SpinBox::_line_edit_input(const InputEvent& p_event) { + + + +} + + void SpinBox::_input_event(const InputEvent& p_event) { if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.pressed) { @@ -94,6 +101,48 @@ void SpinBox::_input_event(const InputEvent& p_event) { } break; } } + + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.pressed && p_event.mouse_button.button_index==1) { + + //set_default_cursor_shape(CURSOR_VSIZE); + Vector2 cpos = Vector2(p_event.mouse_button.x,p_event.mouse_button.y); + drag.mouse_pos=cpos; + } + + if (p_event.type==InputEvent::MOUSE_BUTTON && !p_event.mouse_button.pressed && p_event.mouse_button.button_index==1) { + + //set_default_cursor_shape(CURSOR_ARROW); + if (drag.enabled) { + drag.enabled=false; + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); + warp_mouse(drag.capture_pos); + } + } + + if (p_event.type==InputEvent::MOUSE_MOTION && p_event.mouse_button.button_mask&1) { + + Vector2 cpos = Vector2(p_event.mouse_motion.x,p_event.mouse_motion.y); + if (drag.enabled) { + + float diff_y = drag.mouse_pos.y - cpos.y; + diff_y=Math::pow(ABS(diff_y),1.8)*SGN(diff_y); + diff_y*=0.1; + + drag.mouse_pos=cpos; + drag.base_val=CLAMP(drag.base_val + get_step() * diff_y, get_min(), get_max()); + + set_val( drag.base_val); + + } else if (drag.mouse_pos.distance_to(cpos)>2) { + + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED); + drag.enabled=true; + drag.base_val=get_val(); + drag.mouse_pos=cpos; + drag.capture_pos=cpos; + + } + } } @@ -177,6 +226,7 @@ void SpinBox::_bind_methods() { ObjectTypeDB::bind_method(_MD("is_editable"),&SpinBox::is_editable); ObjectTypeDB::bind_method(_MD("_line_edit_focus_exit"),&SpinBox::_line_edit_focus_exit); ObjectTypeDB::bind_method(_MD("get_line_edit"),&SpinBox::get_line_edit); + ObjectTypeDB::bind_method(_MD("_line_edit_input"),&SpinBox::_line_edit_input); ADD_PROPERTY(PropertyInfo(Variant::BOOL,"editable"),_SCS("set_editable"),_SCS("is_editable")); @@ -196,4 +246,6 @@ SpinBox::SpinBox() { //connect("value_changed",this,"_value_changed"); line_edit->connect("text_entered",this,"_text_entered",Vector<Variant>(),CONNECT_DEFERRED); line_edit->connect("focus_exit",this,"_line_edit_focus_exit",Vector<Variant>(),CONNECT_DEFERRED); + line_edit->connect("input_event",this,"_line_edit_input"); + drag.enabled=false; } diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index 70121c9088..4c8cb8432a 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -44,6 +44,18 @@ class SpinBox : public Range { String prefix; String suffix; + void _line_edit_input(const InputEvent& p_event); + + + struct Drag { + float base_val; + bool enabled; + Vector2 from; + Vector2 mouse_pos; + Vector2 capture_pos; + } drag; + + void _line_edit_focus_exit(); protected: diff --git a/scene/gui/split_container.cpp b/scene/gui/split_container.cpp index 49a7957d7c..49067bb3a0 100644 --- a/scene/gui/split_container.cpp +++ b/scene/gui/split_container.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -345,6 +345,7 @@ void SplitContainer::_input_event(const InputEvent& p_event) { expand_ofs=drag_ofs+((vertical?mm.y:mm.x)-drag_from); queue_sort(); + emit_signal("dragged",get_split_offset()); } } @@ -431,10 +432,12 @@ void SplitContainer::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_dragger_visible","visible"),&SplitContainer::set_dragger_visible); ObjectTypeDB::bind_method(_MD("is_dragger_visible"),&SplitContainer::is_dragger_visible); + ADD_SIGNAL( MethodInfo("dragged",PropertyInfo(Variant::INT,"offset"))); ADD_PROPERTY( PropertyInfo(Variant::INT,"split/offset"),_SCS("set_split_offset"),_SCS("get_split_offset")); - ADD_PROPERTY( PropertyInfo(Variant::INT,"split/collapsed"),_SCS("set_collapsed"),_SCS("is_collapsed")); - ADD_PROPERTY( PropertyInfo(Variant::INT,"split/dragger_visible"),_SCS("set_dragger_visible"),_SCS("is_dragger_visible")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"split/collapsed"),_SCS("set_collapsed"),_SCS("is_collapsed")); + ADD_PROPERTY( PropertyInfo(Variant::BOOL,"split/dragger_visible"),_SCS("set_dragger_visible"),_SCS("is_dragger_visible")); + } diff --git a/scene/gui/split_container.h b/scene/gui/split_container.h index 4065b7818c..d7f56c6fb4 100644 --- a/scene/gui/split_container.h +++ b/scene/gui/split_container.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 2d6f3cd27a..6fa701340d 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -88,7 +88,22 @@ void TabContainer::_input_event(const InputEvent& p_event) { Ref<Font> font = get_font("font"); Ref<Texture> incr = get_icon("increment"); Ref<Texture> decr = get_icon("decrement"); + Ref<Texture> menu = get_icon("menu"); + Ref<Texture> menu_hl = get_icon("menu_hl"); + if (popup && pos.x>get_size().width-menu->get_width()) { + + + emit_signal("pre_popup_pressed"); + Vector2 pp_pos = get_global_pos(); + pp_pos.x+=get_size().width; + pp_pos.x-=popup->get_size().width; + pp_pos.y+=menu->get_height(); + + popup->set_global_pos( pp_pos ); + popup->popup();; + return; + } pos.x-=tabs_ofs_cache; int idx=0; @@ -116,17 +131,17 @@ void TabContainer::_input_event(const InputEvent& p_event) { String s = c->has_meta("_tab_name")?String(XL_MESSAGE(String(c->get_meta("_tab_name")))):String(c->get_name()); int tab_width=font->get_string_size(s).width; - if (c->has_meta("_tab_icon")) { - Ref<Texture> icon = c->get_meta("_tab_icon"); - if (icon.is_valid()) { - tab_width+=icon->get_width(); - if (s!="") - tab_width+=get_constant("hseparation"); + if (c->has_meta("_tab_icon")) { + Ref<Texture> icon = c->get_meta("_tab_icon"); + if (icon.is_valid()) { + tab_width+=icon->get_width(); + if (s!="") + tab_width+=get_constant("hseparation"); - } - } + } + } - if (idx==current) { + if (idx==current) { tab_width+=tab_fg->get_minimum_size().width; } else { @@ -163,7 +178,7 @@ void TabContainer::_input_event(const InputEvent& p_event) { if (found!=-1) { - set_current_tab(found); + set_current_tab(found); } } @@ -194,7 +209,9 @@ void TabContainer::_notification(int p_what) { Ref<Texture> incr = get_icon("increment"); Ref<Texture> incr_hl = get_icon("increment_hilite"); Ref<Texture> decr = get_icon("decrement"); - Ref<Texture> decr_hl = get_icon("decrement_hilite"); + Ref<Texture> decr_hl = get_icon("decrement_hilite"); + Ref<Texture> menu = get_icon("menu"); + Ref<Texture> menu_hl = get_icon("menu_hl"); Ref<Font> font = get_font("font"); Color color_fg = get_color("font_color_fg"); Color color_bg = get_color("font_color_bg"); @@ -209,8 +226,21 @@ void TabContainer::_notification(int p_what) { Size2 top_size = Size2( size.width, top_margin ); + int w=0; int idx=0; + Vector<int> offsets; + Vector<Control*> controls; + int from=0; + int limit=get_size().width; + if (popup) { + top_size.width-=menu->get_width(); + limit-=menu->get_width(); + } + + bool notdone=false; + last_tab_cache=-1; + for(int i=0;i<get_child_count();i++) { Control *c = get_child(i)->cast_to<Control>(); @@ -218,7 +248,20 @@ void TabContainer::_notification(int p_what) { continue; if (c->is_set_as_toplevel()) continue; + if (idx<tab_display_ofs) { + idx++; + from=idx; + continue; + } + + if (w>=get_size().width) { + buttons_visible_cache=true; + notdone=true; + break; + } + offsets.push_back(w); + controls.push_back(c); String s = c->has_meta("_tab_name")?String(XL_MESSAGE(String(c->get_meta("_tab_name")))):String(c->get_name()); w+=font->get_string_size(s).width; @@ -227,7 +270,7 @@ void TabContainer::_notification(int p_what) { if (icon.is_valid()) { w+=icon->get_width(); if (s!="") - w+=get_constant("hseparation"); + w+=get_constant("hseparation"); } } @@ -239,51 +282,46 @@ void TabContainer::_notification(int p_what) { w+=tab_bg->get_minimum_size().width; } + if (idx<tab_display_ofs) { + + } + last_tab_cache=idx; + idx++; } int ofs; - int limit=get_size().width; - - - if (w<=get_size().width) { - switch(align) { - case ALIGN_LEFT: ofs = side_margin; break; - case ALIGN_CENTER: ofs = (int(top_size.width) - w)/2; break; - case ALIGN_RIGHT: ofs = int(top_size.width) - w - side_margin; break; - }; + switch(align) { - tab_display_ofs=0; - buttons_visible_cache=false; - } else { + case ALIGN_LEFT: ofs = side_margin; break; + case ALIGN_CENTER: ofs = (int(limit) - w)/2; break; + case ALIGN_RIGHT: ofs = int(limit) - w - side_margin; break; + }; - ofs=0; - limit-=incr->get_width()+decr->get_width(); - buttons_visible_cache=true; - } + tab_display_ofs=0; tabs_ofs_cache=ofs; - last_tab_cache=-1; idx=0; - bool notdone=false; - for(int i=0;i<get_child_count();i++) { - Control *c = get_child(i)->cast_to<Control>(); - if (!c) - continue; - if (c->is_set_as_toplevel()) - continue; + for(int i=0;i<controls.size();i++) { - if (idx<tab_display_ofs) { - idx++; - continue; + idx=i+from; + if (current>=from && current<from+controls.size()-1) { + //current is visible! draw it last. + if (i==controls.size()-1) { + idx=current; + } else if (idx>=current) { + idx+=1; + } } + Control *c = controls[idx-from]; + String s = c->has_meta("_tab_name")?String(c->get_meta("_tab_name")):String(c->get_name()); int w=font->get_string_size(s).width; Ref<Texture> icon; @@ -314,14 +352,12 @@ void TabContainer::_notification(int p_what) { col=color_bg; } + int lofs = ofs + offsets[idx-from]; Size2i sb_ms = sb->get_minimum_size(); - Rect2 sb_rect = Rect2( ofs, 0, w+sb_ms.width, top_margin); + Rect2 sb_rect = Rect2( lofs, 0, w+sb_ms.width, top_margin); + - if (sb_ms.width+w+ofs > limit) { - notdone=true; - break; - } sb->draw(ci, sb_rect ); Point2i lpos = sb_rect.pos; @@ -335,8 +371,7 @@ void TabContainer::_notification(int p_what) { } font->draw(ci, Point2i( lpos.x, sb->get_margin(MARGIN_TOP)+((sb_rect.size.y-sb_ms.y)-font->get_height())/2+font->get_ascent() ), s, col ); - ofs+=sb_ms.x+w; - last_tab_cache=idx; + //ofs+=sb_ms.x+w; /* int sb_mw = sb->get_minimum_size().width; @@ -364,6 +399,15 @@ void TabContainer::_notification(int p_what) { incr->draw(ci,Point2(limit+incr->get_width(),vofs),Color(1,1,1,notdone?1.0:0.5)); } + if (popup) { + int from = get_size().width-menu->get_width(); + + if (mouse_x_cache > from) + menu_hl->draw(get_canvas_item(),Size2(from,0)); + else + menu->draw(get_canvas_item(),Size2(from,0)); + } + panel->draw(ci, Rect2( 0, top_size.height, size.width, size.height-top_size.height)); } break; @@ -465,6 +509,48 @@ int TabContainer::get_current_tab() const { return current; } +Control* TabContainer::get_tab_control(int p_idx) const { + + int idx=0; + + + for(int i=0;i<get_child_count();i++) { + + Control *c = get_child(i)->cast_to<Control>(); + if (!c) + continue; + if (c->is_set_as_toplevel()) + continue; + if (idx==p_idx) { + return c; + + } + idx++; + } + + return NULL; +} +Control* TabContainer::get_current_tab_control() const { + + int idx=0; + + + for(int i=0;i<get_child_count();i++) { + + Control *c = get_child(i)->cast_to<Control>(); + if (!c) + continue; + if (c->is_set_as_toplevel()) + continue; + if (idx==current) { + return c; + + } + idx++; + } + + return NULL; +} void TabContainer::remove_child_notify(Node *p_child) { @@ -602,12 +688,58 @@ void TabContainer::get_translatable_strings(List<String> *p_strings) const { } +Size2 TabContainer::get_minimum_size() const { + + Size2 ms; + + for(int i=0;i<get_child_count();i++) { + + Control *c = get_child(i)->cast_to<Control>(); + if (!c) + continue; + if (c->is_set_as_toplevel()) + continue; + + if (!c->has_meta("_tab_name")) + continue; + + if (!c->is_visible()) + continue; + + Size2 cms = c->get_minimum_size(); + ms.x=MAX(ms.x,cms.x); + ms.y=MAX(ms.y,cms.y); + } + + Ref<StyleBox> tab_bg = get_stylebox("tab_bg"); + Ref<StyleBox> tab_fg = get_stylebox("tab_fg"); + Ref<Font> font = get_font("font"); + + ms.y+=MAX(tab_bg->get_minimum_size().y,tab_fg->get_minimum_size().y); + ms.y+=font->get_height(); + + return ms; +} + +void TabContainer::set_popup(Node *p_popup) { + ERR_FAIL_NULL(p_popup); + popup=p_popup->cast_to<Popup>(); + update(); +} + +Popup* TabContainer::get_popup() const { + return popup; +} + + void TabContainer::_bind_methods() { ObjectTypeDB::bind_method(_MD("_input_event"),&TabContainer::_input_event); ObjectTypeDB::bind_method(_MD("get_tab_count"),&TabContainer::get_tab_count); ObjectTypeDB::bind_method(_MD("set_current_tab","tab_idx"),&TabContainer::set_current_tab); ObjectTypeDB::bind_method(_MD("get_current_tab"),&TabContainer::get_current_tab); + ObjectTypeDB::bind_method(_MD("get_current_tab_control:Control"),&TabContainer::get_current_tab_control); + ObjectTypeDB::bind_method(_MD("get_tab_control:Control","idx"),&TabContainer::get_tab_control); ObjectTypeDB::bind_method(_MD("set_tab_align","align"),&TabContainer::set_tab_align); ObjectTypeDB::bind_method(_MD("get_tab_align"),&TabContainer::get_tab_align); ObjectTypeDB::bind_method(_MD("set_tabs_visible","visible"),&TabContainer::set_tabs_visible); @@ -616,10 +748,13 @@ void TabContainer::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_tab_title","tab_idx"),&TabContainer::get_tab_title); ObjectTypeDB::bind_method(_MD("set_tab_icon","tab_idx","icon:Texture"),&TabContainer::set_tab_icon); ObjectTypeDB::bind_method(_MD("get_tab_icon:Texture","tab_idx"),&TabContainer::get_tab_icon); + ObjectTypeDB::bind_method(_MD("set_popup","popup:Popup"),&TabContainer::set_popup); + ObjectTypeDB::bind_method(_MD("get_popup:Popup"),&TabContainer::get_popup); ObjectTypeDB::bind_method(_MD("_child_renamed_callback"),&TabContainer::_child_renamed_callback); ADD_SIGNAL(MethodInfo("tab_changed",PropertyInfo(Variant::INT,"tab"))); + ADD_SIGNAL(MethodInfo("pre_popup_pressed")); ADD_PROPERTY( PropertyInfo(Variant::INT, "tab_align", PROPERTY_HINT_ENUM,"Left,Center,Right"), _SCS("set_tab_align"), _SCS("get_tab_align") ); ADD_PROPERTY( PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE,"-1,4096,1",PROPERTY_USAGE_EDITOR), _SCS("set_current_tab"), _SCS("get_current_tab") ); @@ -636,5 +771,6 @@ TabContainer::TabContainer() { mouse_x_cache=0; align=ALIGN_CENTER; tabs_visible=true; + popup=NULL; } diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index d5b6a2b503..602d248b46 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,7 +31,7 @@ #include "scene/gui/control.h" - +#include "scene/gui/popup.h" class TabContainer : public Control { OBJ_TYPE( TabContainer, Control ); @@ -55,6 +55,8 @@ private: TabAlign align; Control *_get_tab(int idx) const; int _get_top_margin() const; + Popup *popup; + protected: @@ -85,8 +87,17 @@ public: void set_current_tab(int p_current); int get_current_tab() const; + Control* get_tab_control(int p_idx) const; + Control* get_current_tab_control() const; + + virtual Size2 get_minimum_size() const; + virtual void get_translatable_strings(List<String> *p_strings) const; + void set_popup(Node *p_popup); + Popup* get_popup() const; + + TabContainer(); }; diff --git a/scene/gui/tabs.cpp b/scene/gui/tabs.cpp index ae7a4d59a7..47a55e0716 100644 --- a/scene/gui/tabs.cpp +++ b/scene/gui/tabs.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -56,7 +56,22 @@ Size2 Tabs::get_minimum_size() const { else ms.width+=tab_bg->get_minimum_size().width; + if (tabs[i].right_button.is_valid()) { + Ref<Texture> rb=tabs[i].right_button; + Size2 bms = rb->get_size();//+get_stylebox("button")->get_minimum_size(); + bms.width+=get_constant("hseparation"); + ms.width+=bms.width; + ms.height=MAX(bms.height+tab_bg->get_minimum_size().height,ms.height); + } + + if (tabs[i].close_button.is_valid()) { + Ref<Texture> cb=tabs[i].close_button; + Size2 bms = cb->get_size();//+get_stylebox("button")->get_minimum_size(); + bms.width+=get_constant("hseparation"); + ms.width+=bms.width; + ms.height=MAX(bms.height+tab_bg->get_minimum_size().height,ms.height); + } } return ms; @@ -66,6 +81,81 @@ Size2 Tabs::get_minimum_size() const { void Tabs::_input_event(const InputEvent& p_event) { + if (p_event.type==InputEvent::MOUSE_MOTION) { + + Point2 pos( p_event.mouse_motion.x, p_event.mouse_motion.y ); + + int hover_buttons=-1; + hover=-1; + for(int i=0;i<tabs.size();i++) { + + // test hovering tab to display close button if policy says so + if (cb_displaypolicy == SHOW_HOVER) { + int ofs=tabs[i].ofs_cache; + int size = tabs[i].ofs_cache; + if (pos.x >=tabs[i].ofs_cache && pos.x<tabs[i].ofs_cache+tabs[i].size_cache) { + hover=i; + } + } + + + // test hovering right button and close button + if (tabs[i].rb_rect.has_point(pos)) { + rb_hover=i; + cb_hover=-1; + hover_buttons = i; + break; + } + else if (tabs[i].cb_rect.has_point(pos)) { + cb_hover=i; + rb_hover=-1; + hover_buttons = i; + break; + } + + + + } + + if (hover_buttons == -1) { // no hover + rb_hover= hover_buttons; + cb_hover= hover_buttons; + } + update(); + + return; + } + + + + + if (rb_pressing && p_event.type==InputEvent::MOUSE_BUTTON && + !p_event.mouse_button.pressed && + p_event.mouse_button.button_index==BUTTON_LEFT) { + + if (rb_hover!=-1) { + //pressed + emit_signal("right_button_pressed",rb_hover); + } + + rb_pressing=false; + update(); + } + + if (cb_pressing && p_event.type==InputEvent::MOUSE_BUTTON && + !p_event.mouse_button.pressed && + p_event.mouse_button.button_index==BUTTON_LEFT) { + + if (cb_hover!=-1) { + //pressed + emit_signal("tab_close",cb_hover); + } + + cb_pressing=false; + update(); + } + + if (p_event.type==InputEvent::MOUSE_BUTTON && p_event.mouse_button.pressed && p_event.mouse_button.button_index==BUTTON_LEFT) { @@ -76,9 +166,21 @@ void Tabs::_input_event(const InputEvent& p_event) { int found=-1; for(int i=0;i<tabs.size();i++) { - int ofs=tabs[i].ofs_cache; + if (tabs[i].rb_rect.has_point(pos)) { + rb_pressing=true; + update(); + return; + } + + if (tabs[i].cb_rect.has_point(pos)) { + cb_pressing=true; + update(); + return; + } - if (pos.x < ofs) { + int ofs=tabs[i].ofs_cache; + int size = tabs[i].ofs_cache; + if (pos.x >=tabs[i].ofs_cache && pos.x<tabs[i].ofs_cache+tabs[i].size_cache) { found=i; break; @@ -89,6 +191,7 @@ void Tabs::_input_event(const InputEvent& p_event) { if (found!=-1) { set_current_tab(found); + emit_signal("tab_changed",found); } } @@ -99,7 +202,12 @@ void Tabs::_notification(int p_what) { switch(p_what) { - + case NOTIFICATION_MOUSE_EXIT: { + rb_hover=-1; + cb_hover=-1; + hover=-1; + update(); + } break; case NOTIFICATION_DRAW: { RID ci = get_canvas_item(); @@ -117,17 +225,31 @@ void Tabs::_notification(int p_what) { int w=0; + int mw = get_minimum_size().width; + + if (tab_align==ALIGN_CENTER) { + w=(get_size().width-mw)/2; + } else if (tab_align==ALIGN_RIGHT) { + w=get_size().width-mw; + + } + + if (w<0) { + w=0; + } + for(int i=0;i<tabs.size();i++) { + tabs[i].ofs_cache=w; String s = tabs[i].text; int lsize=0; - int slen=font->get_string_size(s).width;; + int slen=font->get_string_size(s).width; lsize+=slen; Ref<Texture> icon; if (tabs[i].icon.is_valid()) { - Ref<Texture> icon = tabs[i].icon; + icon = tabs[i].icon; if (icon.is_valid()) { lsize+=icon->get_width(); if (s!="") @@ -136,6 +258,66 @@ void Tabs::_notification(int p_what) { } } + if (tabs[i].right_button.is_valid()) { + Ref<StyleBox> style = get_stylebox("button"); + Ref<Texture> rb=tabs[i].right_button; + + lsize+=get_constant("hseparation"); + //lsize+=style->get_margin(MARGIN_LEFT); + lsize+=rb->get_width(); + //lsize+=style->get_margin(MARGIN_RIGHT); + + } + + // Close button + switch (cb_displaypolicy) { + case SHOW_ALWAYS: { + if (tabs[i].close_button.is_valid()) { + Ref<StyleBox> style = get_stylebox("button"); + Ref<Texture> rb=tabs[i].close_button; + + lsize+=get_constant("hseparation"); + //lsize+=style->get_margin(MARGIN_LEFT); + lsize+=rb->get_width(); + //lsize+=style->get_margin(MARGIN_RIGHT); + + } + } break; + case SHOW_ACTIVE_ONLY: { + if (i==current) { + if (tabs[i].close_button.is_valid()) { + Ref<StyleBox> style = get_stylebox("button"); + Ref<Texture> rb=tabs[i].close_button; + + lsize+=get_constant("hseparation"); + //lsize+=style->get_margin(MARGIN_LEFT); + lsize+=rb->get_width(); + //lsize+=style->get_margin(MARGIN_RIGHT); + + } + } + } break; + case SHOW_HOVER: { + if (i==current || i==hover) { + if (tabs[i].close_button.is_valid()) { + Ref<StyleBox> style = get_stylebox("button"); + Ref<Texture> rb=tabs[i].close_button; + + lsize+=get_constant("hseparation"); + //lsize+=style->get_margin(MARGIN_LEFT); + lsize+=rb->get_width(); + //lsize+=style->get_margin(MARGIN_RIGHT); + + } + } + } break; + case SHOW_NEVER: // by default, never show close button + default: { + // do nothing + } break; + + } + Ref<StyleBox> sb; int va; @@ -169,9 +351,136 @@ void Tabs::_notification(int p_what) { font->draw(ci, Point2i( w, sb->get_margin(MARGIN_TOP)+((sb_rect.size.y-sb_ms.y)-font->get_height())/2+font->get_ascent() ), s, col ); - w+=slen+sb->get_margin(MARGIN_RIGHT); + w+=slen; - tabs[i].ofs_cache=w; + if (tabs[i].right_button.is_valid()) { + Ref<StyleBox> style = get_stylebox("button"); + Ref<Texture> rb=tabs[i].right_button; + + w+=get_constant("hseparation"); + + Rect2 rb_rect; + rb_rect.size=style->get_minimum_size()+rb->get_size(); + rb_rect.pos.x=w; + rb_rect.pos.y=sb->get_margin(MARGIN_TOP)+((sb_rect.size.y-sb_ms.y)-(rb_rect.size.y))/2; + + if (rb_hover==i) { + if (rb_pressing) + get_stylebox("button_pressed")->draw(ci,rb_rect); + else + style->draw(ci,rb_rect); + } + + w+=style->get_margin(MARGIN_LEFT); + + rb->draw(ci,Point2i( w,rb_rect.pos.y+style->get_margin(MARGIN_TOP) )); + w+=rb->get_width(); + w+=style->get_margin(MARGIN_RIGHT); + tabs[i].rb_rect=rb_rect; + + + } + + + + + // Close button + switch (cb_displaypolicy) { + case SHOW_ALWAYS: { + if (tabs[i].close_button.is_valid()) { + Ref<StyleBox> style = get_stylebox("button"); + Ref<Texture> cb=tabs[i].close_button; + + w+=get_constant("hseparation"); + + Rect2 cb_rect; + cb_rect.size=style->get_minimum_size()+cb->get_size(); + cb_rect.pos.x=w; + cb_rect.pos.y=sb->get_margin(MARGIN_TOP)+((sb_rect.size.y-sb_ms.y)-(cb_rect.size.y))/2; + + if (cb_hover==i) { + if (cb_pressing) + get_stylebox("button_pressed")->draw(ci,cb_rect); + else + style->draw(ci,cb_rect); + } + + //w+=style->get_margin(MARGIN_LEFT); + + cb->draw(ci,Point2i( w,cb_rect.pos.y+style->get_margin(MARGIN_TOP) )); + w+=cb->get_width(); + //w+=style->get_margin(MARGIN_RIGHT); + tabs[i].cb_rect=cb_rect; + } + } break; + case SHOW_ACTIVE_ONLY: { + if (current==i) { + if (tabs[i].close_button.is_valid()) { + Ref<StyleBox> style = get_stylebox("button"); + Ref<Texture> cb=tabs[i].close_button; + + w+=get_constant("hseparation"); + + Rect2 cb_rect; + cb_rect.size=style->get_minimum_size()+cb->get_size(); + cb_rect.pos.x=w; + cb_rect.pos.y=sb->get_margin(MARGIN_TOP)+((sb_rect.size.y-sb_ms.y)-(cb_rect.size.y))/2; + + if (cb_hover==i) { + if (cb_pressing) + get_stylebox("button_pressed")->draw(ci,cb_rect); + else + style->draw(ci,cb_rect); + } + + //w+=style->get_margin(MARGIN_LEFT); + + cb->draw(ci,Point2i( w,cb_rect.pos.y+style->get_margin(MARGIN_TOP) )); + w+=cb->get_width(); + //w+=style->get_margin(MARGIN_RIGHT); + tabs[i].cb_rect=cb_rect; + } + } + } break; + case SHOW_HOVER: { + if (current==i || hover==i) { + if (tabs[i].close_button.is_valid()) { + Ref<StyleBox> style = get_stylebox("button"); + Ref<Texture> cb=tabs[i].close_button; + + w+=get_constant("hseparation"); + + Rect2 cb_rect; + cb_rect.size=style->get_minimum_size()+cb->get_size(); + cb_rect.pos.x=w; + cb_rect.pos.y=sb->get_margin(MARGIN_TOP)+((sb_rect.size.y-sb_ms.y)-(cb_rect.size.y))/2; + + if (cb_hover==i) { + if (cb_pressing) + get_stylebox("button_pressed")->draw(ci,cb_rect); + else + style->draw(ci,cb_rect); + } + + //w+=style->get_margin(MARGIN_LEFT); + + cb->draw(ci,Point2i( w,cb_rect.pos.y+style->get_margin(MARGIN_TOP) )); + w+=cb->get_width(); + //w+=style->get_margin(MARGIN_RIGHT); + tabs[i].cb_rect=cb_rect; + } + } + } break; + case SHOW_NEVER: + default: { + // show nothing + } break; + + } + + w+=sb->get_margin(MARGIN_RIGHT); + + tabs[i].size_cache=w-tabs[i].ofs_cache; } @@ -195,7 +504,7 @@ void Tabs::set_current_tab(int p_current) { current=p_current; _change_notify("current_tab"); - emit_signal("tab_changed",current); + //emit_signal("tab_changed",current); update(); } @@ -237,11 +546,46 @@ Ref<Texture> Tabs::get_tab_icon(int p_tab) const{ } + + +void Tabs::set_tab_right_button(int p_tab,const Ref<Texture>& p_right_button){ + + ERR_FAIL_INDEX(p_tab,tabs.size()); + tabs[p_tab].right_button=p_right_button; + update(); + minimum_size_changed(); + +} +Ref<Texture> Tabs::get_tab_right_button(int p_tab) const{ + + ERR_FAIL_INDEX_V(p_tab,tabs.size(),Ref<Texture>()); + return tabs[p_tab].right_button; + +} + +void Tabs::set_tab_close_button(int p_tab, const Ref<Texture>& p_close_button) { + ERR_FAIL_INDEX(p_tab, tabs.size()); + tabs[p_tab].close_button=p_close_button; + update(); + minimum_size_changed(); +} + + +Ref<Texture> Tabs::get_tab_close_button(int p_tab) const{ + + ERR_FAIL_INDEX_V(p_tab,tabs.size(),Ref<Texture>()); + return tabs[p_tab].close_button; + +} + void Tabs::add_tab(const String& p_str,const Ref<Texture>& p_icon) { Tab t; t.text=p_str; t.icon=p_icon; + + t.close_button = get_icon("Close","EditorIcons"); + tabs.push_back(t); update(); @@ -249,6 +593,12 @@ void Tabs::add_tab(const String& p_str,const Ref<Texture>& p_icon) { } +void Tabs::clear_tabs() { + tabs.clear(); + current=0; + update(); +} + void Tabs::remove_tab(int p_idx) { ERR_FAIL_INDEX(p_idx,tabs.size()); @@ -263,10 +613,26 @@ void Tabs::remove_tab(int p_idx) { if (current>=tabs.size()) current=tabs.size()-1; - emit_signal("tab_changed",current); + //emit_signal("tab_changed",current); } +void Tabs::set_tab_close_display_policy(CloseButtonDisplayPolicy p_cb_displaypolicy) { + cb_displaypolicy = p_cb_displaypolicy; +} + + +void Tabs::set_tab_align(TabAlign p_align) { + + tab_align=p_align; + update(); +} + +Tabs::TabAlign Tabs::get_tab_align() const { + + return tab_align; +} + void Tabs::_bind_methods() { @@ -280,15 +646,35 @@ void Tabs::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_tab_icon:Texture","tab_idx"),&Tabs::get_tab_icon); ObjectTypeDB::bind_method(_MD("remove_tab","tab_idx"),&Tabs::remove_tab); ObjectTypeDB::bind_method(_MD("add_tab","title","icon:Texture"),&Tabs::add_tab); + ObjectTypeDB::bind_method(_MD("set_tab_align","align"),&Tabs::set_tab_align); + ObjectTypeDB::bind_method(_MD("get_tab_align"),&Tabs::get_tab_align); ADD_SIGNAL(MethodInfo("tab_changed",PropertyInfo(Variant::INT,"tab"))); + ADD_SIGNAL(MethodInfo("right_button_pressed",PropertyInfo(Variant::INT,"tab"))); + ADD_SIGNAL(MethodInfo("tab_close",PropertyInfo(Variant::INT,"tab"))); + ADD_PROPERTY( PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE,"-1,4096,1",PROPERTY_USAGE_EDITOR), _SCS("set_current_tab"), _SCS("get_current_tab") ); + BIND_CONSTANT( ALIGN_LEFT ); + BIND_CONSTANT( ALIGN_CENTER ); + BIND_CONSTANT( ALIGN_RIGHT ); + + BIND_CONSTANT( SHOW_ACTIVE_ONLY ); + BIND_CONSTANT( SHOW_ALWAYS ); + BIND_CONSTANT( SHOW_HOVER ); + BIND_CONSTANT( SHOW_NEVER ); } + Tabs::Tabs() { current=0; + tab_align=ALIGN_CENTER; + rb_hover=-1; + rb_pressing=false; + cb_hover=-1; + cb_pressing=false; + cb_displaypolicy = SHOW_NEVER; // Default : no close button } diff --git a/scene/gui/tabs.h b/scene/gui/tabs.h index 72c077a8b0..1a8352bc93 100644 --- a/scene/gui/tabs.h +++ b/scene/gui/tabs.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -34,6 +34,22 @@ class Tabs : public Control { OBJ_TYPE( Tabs, Control ); +public: + + enum TabAlign { + + ALIGN_LEFT, + ALIGN_CENTER, + ALIGN_RIGHT + }; + + enum CloseButtonDisplayPolicy { + + SHOW_ALWAYS, + SHOW_ACTIVE_ONLY, + SHOW_HOVER, + SHOW_NEVER + }; private: @@ -42,12 +58,26 @@ private: String text; Ref<Texture> icon; int ofs_cache; + int size_cache; + Ref<Texture> right_button; + Rect2 rb_rect; + Ref<Texture> close_button; + Rect2 cb_rect; }; Vector<Tab> tabs; int current; Control *_get_tab(int idx) const; int _get_top_margin() const; + TabAlign tab_align; + int rb_hover; + bool rb_pressing; + + int cb_hover; + bool cb_pressing; + CloseButtonDisplayPolicy cb_displaypolicy; + + int hover; // hovered tab protected: @@ -65,16 +95,29 @@ public: void set_tab_icon(int p_tab,const Ref<Texture>& p_icon); Ref<Texture> get_tab_icon(int p_tab) const; + void set_tab_right_button(int p_tab,const Ref<Texture>& p_right_button); + Ref<Texture> get_tab_right_button(int p_tab) const; + + void set_tab_close_button(int p_tab, const Ref<Texture>& p_close_button); + Ref<Texture> get_tab_close_button(int p_tab) const; + void set_tab_close_display_policy(CloseButtonDisplayPolicy p_cb_displaypolicy); + + void set_tab_align(TabAlign p_align); + TabAlign get_tab_align() const; + int get_tab_count() const; void set_current_tab(int p_current); int get_current_tab() const; void remove_tab(int p_idx); + void clear_tabs(); + Size2 get_minimum_size() const; Tabs(); }; +VARIANT_ENUM_CAST(Tabs::TabAlign); #endif // TABS_H diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 71f0d926c3..5415484009 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -26,16 +26,6 @@ /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /*************************************************************************/ - /*****f********************************************/ -/* text_edit.cpp */ -/*************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/*************************************************/ -/* Source code within this file is: */ -/* (c) 2007-2010 Juan Linietsky, Ariel Manzur */ -/* All Rights Reserved. */ -/*************************************************/ #include "text_edit.h" #include "os/keyboard.h" @@ -47,343 +37,346 @@ #define TAB_PIXELS static bool _is_text_char(CharType c) { - - return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; + + return (c>='a' && c<='z') || (c>='A' && c<='Z') || (c>='0' && c<='9') || c=='_'; } static bool _is_symbol(CharType c) { - - return c!='_' && ((c>='!' && c<='/') || (c>=':' && c<='@') || (c>='[' && c<='`') || (c>='{' && c<='~') || c=='\t'); + + return c!='_' && ((c>='!' && c<='/') || (c>=':' && c<='@') || (c>='[' && c<='`') || (c>='{' && c<='~') || c=='\t'); } static bool _is_pair_right_symbol(CharType c) { - return - c == '"' || - c == '\'' || - c == ')' || - c == ']' || - c == '}'; + return + c == '"' || + c == '\'' || + c == ')' || + c == ']' || + c == '}'; } static bool _is_pair_left_symbol(CharType c) { - return - c == '"' || - c == '\'' || - c == '(' || - c == '[' || - c == '{'; + return + c == '"' || + c == '\'' || + c == '(' || + c == '[' || + c == '{'; } static bool _is_pair_symbol(CharType c) { - return _is_pair_left_symbol(c) || _is_pair_right_symbol(c); + return _is_pair_left_symbol(c) || _is_pair_right_symbol(c); } static CharType _get_right_pair_symbol(CharType c) { - if(c == '"') - return '"'; - if(c == '\'') - return '\''; - if(c == '(') - return ')'; - if(c == '[') - return ']'; - if(c == '{') - return '}'; - return 0; + if(c == '"') + return '"'; + if(c == '\'') + return '\''; + if(c == '(') + return ')'; + if(c == '[') + return ']'; + if(c == '{') + return '}'; + return 0; } void TextEdit::Text::set_font(const Ref<Font>& p_font) { - - font=p_font; + + font=p_font; } void TextEdit::Text::set_tab_size(int p_tab_size) { - - tab_size=p_tab_size; + + tab_size=p_tab_size; } void TextEdit::Text::_update_line_cache(int p_line) const { - - int w =0; - int tab_w=font->get_char_size(' ').width; - - int len = text[p_line].data.length(); - const CharType *str = text[p_line].data.c_str(); - - //update width - - for(int i=0;i<len;i++) { - if (str[i]=='\t') { - - int left = w%tab_w; - if (left==0) - w+=tab_w; - else - w+=tab_w-w%tab_w; // is right... - - } else { - - w+=font->get_char_size(str[i],str[i+1]).width; - } - } - - - text[p_line].width_cache=w; - - //update regions - - text[p_line].region_info.clear(); - - for(int i=0;i<len;i++) { - - if (!_is_symbol(str[i])) - continue; - if (str[i]=='\\') { - i++; //skip quoted anything - continue; - } - - int left=len-i; - - for(int j=0;j<color_regions->size();j++) { - - const ColorRegion& cr=color_regions->operator [](j); - - /* BEGIN */ - - int lr=cr.begin_key.length(); - if (lr==0 || lr>left) - continue; - - const CharType* kc = cr.begin_key.c_str(); - - bool match=true; - - for(int k=0;k<lr;k++) { - if (kc[k]!=str[i+k]) { - match=false; - break; - } - } - - if (match) { - - ColorRegionInfo cri; - cri.end=false; - cri.region=j; - text[p_line].region_info[i]=cri; - i+=lr-1; - break; - } - - /* END */ - - lr=cr.end_key.length(); - if (lr==0 || lr>left) - continue; - - kc = cr.end_key.c_str(); - - match=true; - - for(int k=0;k<lr;k++) { - if (kc[k]!=str[i+k]) { - match=false; - break; - } - } - - if (match) { - - ColorRegionInfo cri; - cri.end=true; - cri.region=j; - text[p_line].region_info[i]=cri; - i+=lr-1; - break; - } - - } - } - - + + int w = 0; + int tab_w=font->get_char_size(' ').width*tab_size; + + int len = text[p_line].data.length(); + const CharType *str = text[p_line].data.c_str(); + + //update width + + for(int i=0;i<len;i++) { + if (str[i]=='\t') { + + int left = w%tab_w; + if (left==0) + w+=tab_w; + else + w+=tab_w-w%tab_w; // is right... + + } else { + + w+=font->get_char_size(str[i],str[i+1]).width; + } + } + + + text[p_line].width_cache=w; + + //update regions + + text[p_line].region_info.clear(); + + for(int i=0;i<len;i++) { + + if (!_is_symbol(str[i])) + continue; + if (str[i]=='\\') { + i++; //skip quoted anything + continue; + } + + int left=len-i; + + for(int j=0;j<color_regions->size();j++) { + + const ColorRegion& cr=color_regions->operator [](j); + + /* BEGIN */ + + int lr=cr.begin_key.length(); + if (lr==0 || lr>left) + continue; + + const CharType* kc = cr.begin_key.c_str(); + + bool match=true; + + for(int k=0;k<lr;k++) { + if (kc[k]!=str[i+k]) { + match=false; + break; + } + } + + if (match) { + + ColorRegionInfo cri; + cri.end=false; + cri.region=j; + text[p_line].region_info[i]=cri; + i+=lr-1; + break; + } + + /* END */ + + lr=cr.end_key.length(); + if (lr==0 || lr>left) + continue; + + kc = cr.end_key.c_str(); + + match=true; + + for(int k=0;k<lr;k++) { + if (kc[k]!=str[i+k]) { + match=false; + break; + } + } + + if (match) { + + ColorRegionInfo cri; + cri.end=true; + cri.region=j; + text[p_line].region_info[i]=cri; + i+=lr-1; + break; + } + + } + } + + } const Map<int,TextEdit::Text::ColorRegionInfo>& TextEdit::Text::get_color_region_info(int p_line) { - - Map<int,ColorRegionInfo> *cri=NULL; - ERR_FAIL_INDEX_V(p_line,text.size(),*cri); //enjoy your crash - - if (text[p_line].width_cache==-1) { - _update_line_cache(p_line); - } - - return text[p_line].region_info; + + Map<int,ColorRegionInfo> *cri=NULL; + ERR_FAIL_INDEX_V(p_line,text.size(),*cri); //enjoy your crash + + if (text[p_line].width_cache==-1) { + _update_line_cache(p_line); + } + + return text[p_line].region_info; } int TextEdit::Text::get_line_width(int p_line) const { - - ERR_FAIL_INDEX_V(p_line,text.size(),-1); - - if (text[p_line].width_cache==-1) { - _update_line_cache(p_line); - } - - return text[p_line].width_cache; + + ERR_FAIL_INDEX_V(p_line,text.size(),-1); + + if (text[p_line].width_cache==-1) { + _update_line_cache(p_line); + } + + return text[p_line].width_cache; } void TextEdit::Text::clear_caches() { - - for(int i=0;i<text.size();i++) - text[i].width_cache=-1; - + + for(int i=0;i<text.size();i++) + text[i].width_cache=-1; + } void TextEdit::Text::clear() { - - - text.clear();; - insert(0,""); + + + text.clear();; + insert(0,""); } int TextEdit::Text::get_max_width() const { - //quite some work.. but should be fast enough. - - int max = 0; - - for(int i=0;i<text.size();i++) - max=MAX(max,get_line_width(i)); - return max; - + //quite some work.. but should be fast enough. + + int max = 0; + + for(int i=0;i<text.size();i++) + max=MAX(max,get_line_width(i)); + return max; + } void TextEdit::Text::set(int p_line,const String& p_text) { - - ERR_FAIL_INDEX(p_line,text.size()); - - text[p_line].width_cache=-1; - text[p_line].data=p_text; + + ERR_FAIL_INDEX(p_line,text.size()); + + text[p_line].width_cache=-1; + text[p_line].data=p_text; } void TextEdit::Text::insert(int p_at,const String& p_text) { - - Line line; - line.marked=false; - line.breakpoint=false; - line.width_cache=-1; - line.data=p_text; - text.insert(p_at,line); + + Line line; + line.marked=false; + line.breakpoint=false; + line.width_cache=-1; + line.data=p_text; + text.insert(p_at,line); } void TextEdit::Text::remove(int p_at) { - - text.remove(p_at); + + text.remove(p_at); } void TextEdit::_update_scrollbars() { - - - Size2 size = get_size(); - Size2 hmin = h_scroll->get_combined_minimum_size(); - Size2 vmin = v_scroll->get_combined_minimum_size(); - - - - v_scroll->set_begin( Point2(size.width - vmin.width, cache.style_normal->get_margin(MARGIN_TOP)) ); - v_scroll->set_end( Point2(size.width, size.height - cache.style_normal->get_margin(MARGIN_TOP) - cache.style_normal->get_margin(MARGIN_BOTTOM)) ); - - h_scroll->set_begin( Point2( 0, size.height - hmin.height) ); - h_scroll->set_end( Point2(size.width-vmin.width, size.height) ); - - - int hscroll_rows = ((hmin.height-1)/get_row_height())+1; - int visible_rows = get_visible_rows(); - int total_rows = text.size() * cache.line_spacing; - - int vscroll_pixels = v_scroll->get_combined_minimum_size().width; - int visible_width = size.width - cache.style_normal->get_minimum_size().width; - int total_width = text.get_max_width(); - - bool use_hscroll=true; - bool use_vscroll=true; - - if (total_rows <= visible_rows && total_width <= visible_width) { - //thanks yessopie for this clever bit of logic - use_hscroll=false; - use_vscroll=false; - - } else { - - if (total_rows > visible_rows && total_width <= visible_width - vscroll_pixels) { - //thanks yessopie for this clever bit of logic - use_hscroll=false; - } - - if (total_rows <= visible_rows - hscroll_rows && total_width > visible_width) { - //thanks yessopie for this clever bit of logic - use_vscroll=false; - } - } - - updating_scrolls=true; - - if (use_vscroll) { - - v_scroll->show(); - v_scroll->set_max(total_rows); - v_scroll->set_page(visible_rows); - - v_scroll->set_val(cursor.line_ofs); - - } else { - cursor.line_ofs = 0; - v_scroll->hide(); - } - - if (use_hscroll) { - - h_scroll->show(); - h_scroll->set_max(total_width); - h_scroll->set_page(visible_width); - h_scroll->set_val(cursor.x_ofs); - } else { - - h_scroll->hide(); - } - - - - updating_scrolls=false; + + + Size2 size = get_size(); + Size2 hmin = h_scroll->get_combined_minimum_size(); + Size2 vmin = v_scroll->get_combined_minimum_size(); + + + + v_scroll->set_begin( Point2(size.width - vmin.width, cache.style_normal->get_margin(MARGIN_TOP)) ); + v_scroll->set_end( Point2(size.width, size.height - cache.style_normal->get_margin(MARGIN_TOP) - cache.style_normal->get_margin(MARGIN_BOTTOM)) ); + + h_scroll->set_begin( Point2( 0, size.height - hmin.height) ); + h_scroll->set_end( Point2(size.width-vmin.width, size.height) ); + + + int hscroll_rows = ((hmin.height-1)/get_row_height())+1; + int visible_rows = get_visible_rows(); + int total_rows = text.size(); + + int vscroll_pixels = v_scroll->get_combined_minimum_size().width; + int visible_width = size.width - cache.style_normal->get_minimum_size().width; + int total_width = text.get_max_width() + vmin.x; + + if (line_numbers) + total_width += cache.line_number_w; + + bool use_hscroll=true; + bool use_vscroll=true; + + if (total_rows <= visible_rows && total_width <= visible_width) { + //thanks yessopie for this clever bit of logic + use_hscroll=false; + use_vscroll=false; + + } else { + + if (total_rows > visible_rows && total_width <= visible_width - vscroll_pixels) { + //thanks yessopie for this clever bit of logic + use_hscroll=false; + } + + if (total_rows <= visible_rows - hscroll_rows && total_width > visible_width) { + //thanks yessopie for this clever bit of logic + use_vscroll=false; + } + } + + updating_scrolls=true; + + if (use_vscroll) { + + v_scroll->show(); + v_scroll->set_max(total_rows); + v_scroll->set_page(visible_rows); + v_scroll->set_val(cursor.line_ofs); + + } else { + cursor.line_ofs = 0; + v_scroll->hide(); + } + + if (use_hscroll) { + + h_scroll->show(); + h_scroll->set_max(total_width); + h_scroll->set_page(visible_width); + h_scroll->set_val(cursor.x_ofs); + + } else { + + h_scroll->hide(); + } + + + + updating_scrolls=false; } void TextEdit::_notification(int p_what) { - + switch(p_what) { case NOTIFICATION_ENTER_TREE: { - + _update_caches(); if (cursor_changed_dirty) MessageQueue::get_singleton()->push_call(this,"_cursor_changed_emit"); if (text_changed_dirty) MessageQueue::get_singleton()->push_call(this,"_text_changed_emit"); - + } break; case NOTIFICATION_RESIZED: { - + cache.size=get_size(); adjust_viewport_to_cursor(); - - + + } break; case NOTIFICATION_THEME_CHANGED: { - + _update_caches(); }; case NOTIFICATION_DRAW: { - + int line_number_char_count=0; - + { int lc=text.size()+1; cache.line_number_w=0; @@ -391,20 +384,20 @@ void TextEdit::_notification(int p_what) { cache.line_number_w+=1; lc/=10; }; - + if (line_numbers) { - + line_number_char_count=cache.line_number_w; cache.line_number_w=(cache.line_number_w+1)*cache.font->get_char_size('0').width; } else { cache.line_number_w=0; } - - + + } _update_scrollbars(); - - + + RID ci = get_canvas_item(); int xmargin_beg=cache.style_normal->get_margin(MARGIN_LEFT)+cache.line_number_w; int xmargin_end=cache.size.width-cache.style_normal->get_margin(MARGIN_RIGHT); @@ -412,56 +405,56 @@ void TextEdit::_notification(int p_what) { cache.style_normal->draw(ci,Rect2(Point2(),cache.size)); if (has_focus()) cache.style_focus->draw(ci,Rect2(Point2(),cache.size)); - - + + int ascent=cache.font->get_ascent(); - + int visible_rows = get_visible_rows(); - + int tab_w = cache.font->get_char_size(' ').width*tab_size; - + Color color = cache.font_color; int in_region=-1; - + if (syntax_coloring) { - + if (custom_bg_color.a>0.01) { - + Point2i ofs = Point2i(cache.style_normal->get_offset())/2.0; VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(ofs, get_size()-cache.style_normal->get_minimum_size()+ofs),custom_bg_color); } //compute actual region to start (may be inside say, a comment). //slow in very large documments :( but ok for source! - + for(int i=0;i<cursor.line_ofs;i++) { - + const Map<int,Text::ColorRegionInfo>& cri_map=text.get_color_region_info(i); - + if (in_region>=0 && color_regions[in_region].line_only) { in_region=-1; //reset regions that end at end of line } - + for( const Map<int,Text::ColorRegionInfo>::Element* E= cri_map.front();E;E=E->next() ) { - + const Text::ColorRegionInfo &cri=E->get(); - + if (in_region==-1) { - + if (!cri.end) { - + in_region=cri.region; } } else if (in_region==cri.region && !color_regions[cri.region].line_only) { //ignore otherwise - + if (cri.end || color_regions[cri.region].eq) { - + in_region=-1; } } } } } - + int brace_open_match_line=-1; int brace_open_match_column=-1; bool brace_open_matching=false; @@ -470,15 +463,15 @@ void TextEdit::_notification(int p_what) { int brace_close_match_column=-1; bool brace_close_matching=false; bool brace_close_mismatch=false; - - + + if (brace_matching_enabled) { - + if (cursor.column<text[cursor.line].length()) { //check for open CharType c = text[cursor.line][cursor.column]; CharType closec=0; - + if (c=='[') { closec=']'; } else if (c=='{') { @@ -486,48 +479,70 @@ void TextEdit::_notification(int p_what) { } else if (c=='(') { closec=')'; } - + if (closec!=0) { - + int stack=1; - - + + for(int i=cursor.line;i<text.size();i++) { - + int from = i==cursor.line?cursor.column+1:0; for(int j=from;j<text[i].length();j++) { - + CharType cc = text[i][j]; - if (cc==c) + //ignore any brackets inside a string + if (cc== '"' || cc == '\'') { + CharType quotation = cc; + do { + j++; + if (!(j<text[i].length())) { + break; + } + cc=text[i][j]; + //skip over escaped quotation marks inside strings + if (cc=='\\') { + bool escaped = true; + while (j+1<text[i].length() && text[i][j+1]=='\\') { + escaped=!escaped; + j++; + } + if (escaped) { + j++; + continue; + } + } + } while (cc!= quotation); + } else if (cc==c) stack++; else if (cc==closec) stack--; - + if (stack==0) { brace_open_match_line=i; brace_open_match_column=j; brace_open_matching=true; - + break; } } if (brace_open_match_line!=-1) break; } - + if (!brace_open_matching) brace_open_mismatch=true; - - + + } } - + if (cursor.column>0) { CharType c = text[cursor.line][cursor.column-1]; CharType closec=0; - - - + + + if (c==']') { closec='['; } else if (c=='}') { @@ -535,65 +550,88 @@ void TextEdit::_notification(int p_what) { } else if (c==')') { closec='('; } - + if (closec!=0) { - + int stack=1; - - + + for(int i=cursor.line;i>=0;i--) { - + int from = i==cursor.line?cursor.column-2:text[i].length()-1; for(int j=from;j>=0;j--) { - + CharType cc = text[i][j]; - if (cc==c) + //ignore any brackets inside a string + if (cc== '"' || cc == '\'') { + CharType quotation = cc; + do { + j--; + if (!(j>=0)) { + break; + } + cc=text[i][j]; + //skip over escaped quotation marks inside strings + if (cc==quotation) { + bool escaped = false; + while (j-1>=0 && text[i][j-1]=='\\') { + escaped=!escaped; + j--; + } + if (escaped) { + j--; + cc='\\'; + continue; + } + } + } while (cc!= quotation); + } else if (cc==c) stack++; else if (cc==closec) stack--; - + if (stack==0) { brace_close_match_line=i; brace_close_match_column=j; brace_close_matching=true; - + break; } } if (brace_close_match_line!=-1) break; } - + if (!brace_close_matching) brace_close_mismatch=true; - - + + } - - + + } } - - + + int deregion=0; //force it to clear inrgion Point2 cursor_pos; - + for (int i=0;i<visible_rows;i++) { - + int line=i+cursor.line_ofs; - + if (line<0 || line>=(int)text.size()) continue; - + const String &str=text[line]; - + int char_margin=xmargin_beg-cursor.x_ofs; int char_ofs=0; int ofs_y=i*get_row_height()+cache.line_spacing/2; bool prev_is_char=false; bool in_keyword=false; Color keyword_color; - + if (cache.line_number_w) { Color fcol = cache.font_color; fcol.a*=0.4; @@ -601,191 +639,191 @@ void TextEdit::_notification(int p_what) { while (fc.length() < line_number_char_count) { fc="0"+fc; } - + cache.font->draw(ci,Point2(cache.style_normal->get_margin(MARGIN_LEFT),ofs_y+cache.font->get_ascent()),fc,fcol); } - + const Map<int,Text::ColorRegionInfo>& cri_map=text.get_color_region_info(line); - - + + if (text.is_marked(line)) { - + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.mark_color); } - + if (text.is_breakpoint(line)) { - + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.breakpoint_color); } - - + + if (line==cursor.line) { - + VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(xmargin_beg, ofs_y,xmargin_end-xmargin_beg,get_row_height()),cache.current_line_color); - + } for (int j=0;j<str.length();j++) { - + //look for keyword - + if (deregion>0) { deregion--; if (deregion==0) in_region=-1; } if (syntax_coloring && deregion==0) { - - + + color = cache.font_color; //reset //find keyword bool is_char = _is_text_char(str[j]); bool is_symbol=_is_symbol(str[j]); - + if (j==0 && in_region>=0 && color_regions[in_region].line_only) { in_region=-1; //reset regions that end at end of line } - + if (is_symbol && cri_map.has(j)) { - - + + const Text::ColorRegionInfo &cri=cri_map[j]; - + if (in_region==-1) { - + if (!cri.end) { - + in_region=cri.region; } } else if (in_region==cri.region && !color_regions[cri.region].line_only) { //ignore otherwise - + if (cri.end || color_regions[cri.region].eq) { - + deregion=color_regions[cri.region].eq?color_regions[cri.region].begin_key.length():color_regions[cri.region].end_key.length(); } } } - + if (!is_char) in_keyword=false; - + if (in_region==-1 && !in_keyword && is_char && !prev_is_char) { - + int to=j; - while(_is_text_char(str[to]) && to<str.length()) + while(to<str.length() && _is_text_char(str[to])) to++; - + uint32_t hash = String::hash(&str[j],to-j); StrRange range(&str[j],to-j); - + const Color *col=keywords.custom_getptr(range,hash); - + if (col) { - + in_keyword=true; keyword_color=*col; } } - - + + if (in_region>=0) color=color_regions[in_region].color; else if (in_keyword) color=keyword_color; else if (is_symbol) color=symbol_color; - + prev_is_char=is_char; - + } int char_w; - + //handle tabulator - - + + if (str[j]=='\t') { int left = char_ofs%tab_w; if (left==0) char_w=tab_w; else char_w=tab_w-char_ofs%tab_w; // is right... - + } else { char_w=cache.font->get_char_size(str[j],str[j+1]).width; } - + if ( (char_ofs+char_margin)<xmargin_beg) { char_ofs+=char_w; continue; } - + if ( (char_ofs+char_margin+char_w)>=xmargin_end) { if (syntax_coloring) continue; else break; } - + bool in_selection = (selection.active && line>=selection.from_line && line<=selection.to_line && (line>selection.from_line || j>=selection.from_column) && (line<selection.to_line || j<selection.to_column)); - - + + if (in_selection) { //inside selection! VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(Point2i( char_ofs+char_margin, ofs_y ), Size2i(char_w,get_row_height())),cache.selection_color); } - - + + if (brace_matching_enabled) { if ( (brace_open_match_line==line && brace_open_match_column==j) || - (cursor.column==j && cursor.line==line && (brace_open_matching||brace_open_mismatch))) { - + (cursor.column==j && cursor.line==line && (brace_open_matching||brace_open_mismatch))) { + if (brace_open_mismatch) color=cache.brace_mismatch_color; cache.font->draw_char(ci,Point2i( char_ofs+char_margin, ofs_y+ascent),'_',str[j+1],in_selection?cache.font_selected_color:color); - + } - + if ( - (brace_close_match_line==line && brace_close_match_column==j) || - (cursor.column==j+1 && cursor.line==line && (brace_close_matching||brace_close_mismatch))) { - - + (brace_close_match_line==line && brace_close_match_column==j) || + (cursor.column==j+1 && cursor.line==line && (brace_close_matching||brace_close_mismatch))) { + + if (brace_close_mismatch) color=cache.brace_mismatch_color; cache.font->draw_char(ci,Point2i( char_ofs+char_margin, ofs_y+ascent),'_',str[j+1],in_selection?cache.font_selected_color:color); - + } } - - + + if (str[j]>=32) cache.font->draw_char(ci,Point2i( char_ofs+char_margin, ofs_y+ascent),str[j],str[j+1],in_selection?cache.font_selected_color:color); - + else if (draw_tabs && str[j]=='\t') { int yofs= (get_row_height() - cache.tab_icon->get_height())/2; cache.tab_icon->draw(ci, Point2(char_ofs+char_margin,ofs_y+yofs),in_selection?cache.font_selected_color:color); } - - + + if (cursor.column==j && cursor.line==line) { - + cursor_pos = Point2i( char_ofs+char_margin, ofs_y ); VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.font_color); - - + + } char_ofs+=char_w; - + } - + if (cursor.column==str.length() && cursor.line==line) { - + cursor_pos=Point2i( char_ofs+char_margin, ofs_y ); VisualServer::get_singleton()->canvas_item_add_rect(ci,Rect2(cursor_pos, Size2i(1,get_row_height())),cache.font_color); - + } } - - + + if (completion_active) { // code completion box Ref<StyleBox> csb = get_stylebox("completion"); @@ -793,17 +831,18 @@ void TextEdit::_notification(int p_what) { int maxlines = get_constant("completion_lines"); int cmax_width = get_constant("completion_max_width")*cache.font->get_char_size('x').x; Color existing = get_color("completion_existing"); + existing.a=0.2; int scrollw = get_constant("completion_scroll_width"); Color scrollc = get_color("completion_scroll_color"); - - - + + + int lines = MIN(completion_options.size(),maxlines); int w=0; int h=lines*get_row_height(); int nofs = cache.font->get_string_size(completion_base).width; - - + + if (completion_options.size() < 50) { for(int i=0;i<completion_options.size();i++) { int w2=MIN(cache.font->get_string_size(completion_options[i]).x,cmax_width); @@ -813,65 +852,74 @@ void TextEdit::_notification(int p_what) { } else { w=cmax_width; } - + int th = h + csb->get_minimum_size().y; if (cursor_pos.y+get_row_height()+th > get_size().height) { completion_rect.pos.y=cursor_pos.y-th; } else { completion_rect.pos.y=cursor_pos.y+get_row_height()+csb->get_offset().y; - + } - + if (cursor_pos.x-nofs+w+scrollw > get_size().width) { completion_rect.pos.x=get_size().width-w-scrollw; } else { completion_rect.pos.x=cursor_pos.x-nofs; } - - completion_rect.size.width=w; + + completion_rect.size.width=w+2; completion_rect.size.height=h; if (completion_options.size()<=maxlines) scrollw=0; - + draw_style_box(csb,Rect2(completion_rect.pos-csb->get_offset(),completion_rect.size+csb->get_minimum_size()+Size2(scrollw,0))); - - + + int line_from = CLAMP(completion_index - lines/2, 0, completion_options.size() - lines); draw_style_box(csel,Rect2(Point2(completion_rect.pos.x,completion_rect.pos.y+(completion_index-line_from)*get_row_height()),Size2(completion_rect.size.width,get_row_height()))); - + draw_rect(Rect2(completion_rect.pos,Size2(nofs,completion_rect.size.height)),existing); - + + + + for(int i=0;i<lines;i++) { - + int l = line_from + i; ERR_CONTINUE( l < 0 || l>= completion_options.size()); - draw_string(cache.font,Point2(completion_rect.pos.x,completion_rect.pos.y+i*get_row_height()+cache.font->get_ascent()),completion_options[l],cache.font_color,completion_rect.size.width); + Color text_color = cache.font_color; + for(int j=0;j<color_regions.size();j++) { + if (completion_options[l].begins_with(color_regions[j].begin_key)) { + text_color=color_regions[j].color; + } + } + draw_string(cache.font,Point2(completion_rect.pos.x,completion_rect.pos.y+i*get_row_height()+cache.font->get_ascent()),completion_options[l],text_color,completion_rect.size.width); } - + if (scrollw) { //draw a small scroll rectangle to show a position in the options float r = maxlines / (float)completion_options.size(); float o = line_from / (float)completion_options.size(); draw_rect(Rect2(completion_rect.pos.x+completion_rect.size.width,completion_rect.pos.y+o*completion_rect.size.y,scrollw,completion_rect.size.y*r),scrollc); } - + completion_line_ofs=line_from; - + } - + if (completion_hint!="") { - + Ref<StyleBox> sb = get_stylebox("panel","TooltipPanel"); Ref<Font> font = cache.font; Color font_color = get_color("font_color","TooltipLabel"); - - + + int max_w=0; int sc = completion_hint.get_slice_count("\n"); int offset=0; int spacing=0; for(int i=0;i<sc;i++) { - + String l = completion_hint.get_slice("\n",i); int len = font->get_string_size(l).x; max_w = MAX(len,max_w); @@ -880,35 +928,35 @@ void TextEdit::_notification(int p_what) { } else { spacing+=cache.line_spacing; } - - + + } - - - + + + Size2 size = Size2(max_w,sc*font->get_height()+spacing); Size2 minsize = size+sb->get_minimum_size(); - - + + if (completion_hint_offset==-0xFFFF) { completion_hint_offset=cursor_pos.x-offset; } - - + + Point2 hint_ofs = Vector2(completion_hint_offset,cursor_pos.y-minsize.y); draw_style_box(sb,Rect2(hint_ofs,minsize)); - + spacing=0; for(int i=0;i<sc;i++) { int begin=0; int end=0; String l = completion_hint.get_slice("\n",i); - + if (l.find(String::chr(0xFFFF))!=-1) { begin = font->get_string_size(l.substr(0,l.find(String::chr(0xFFFF)))).x; end = font->get_string_size(l.substr(0,l.rfind(String::chr(0xFFFF)))).x; } - + draw_string(font,hint_ofs+sb->get_offset()+Vector2(0,font->get_ascent()+font->get_height()*i+spacing),l.replace(String::chr(0xFFFF),""),font_color); if (end>0) { Vector2 b = hint_ofs+sb->get_offset()+Vector2(begin,font->get_height()+font->get_height()*i+spacing-1); @@ -917,1477 +965,1640 @@ void TextEdit::_notification(int p_what) { spacing+=cache.line_spacing; } } - - + + } break; case NOTIFICATION_FOCUS_ENTER: { - + if (OS::get_singleton()->has_virtual_keyboard()) OS::get_singleton()->show_virtual_keyboard(get_text(),get_global_rect()); - + } break; case NOTIFICATION_FOCUS_EXIT: { - + if (OS::get_singleton()->has_virtual_keyboard()) OS::get_singleton()->hide_virtual_keyboard(); - + } break; - + } } void TextEdit::_consume_pair_symbol(CharType ch) { - - int cursor_position_to_move = cursor_get_column() + 1; - - CharType ch_single[2] = {ch, 0}; - CharType ch_single_pair[2] = {_get_right_pair_symbol(ch), 0}; - CharType ch_pair[3] = {ch, _get_right_pair_symbol(ch), 0}; - - if(is_selection_active()) { - - int new_column,new_line; - - _begin_compex_operation(); - _insert_text(get_selection_from_line(), get_selection_from_column(), - ch_single, - &new_line, &new_column); - - int to_col_offset = 0; - if(get_selection_from_line() == get_selection_to_line()) - to_col_offset = 1; - - _insert_text(get_selection_to_line(), - get_selection_to_column() + to_col_offset, - ch_single_pair, - &new_line,&new_column); - _end_compex_operation(); - - cursor_set_line(get_selection_to_line()); - cursor_set_column(get_selection_to_column() + to_col_offset); - - deselect(); - update(); - return; - } - - if( (ch == '\'' || ch == '"') && - cursor_get_column() > 0 && - _is_text_char(text[cursor.line][cursor_get_column() - 1]) - ) { - insert_text_at_cursor(ch_single); - cursor_set_column(cursor_position_to_move); - return; - } - - if(cursor_get_column() < text[cursor.line].length()) { - if(_is_text_char(text[cursor.line][cursor_get_column()])) { - insert_text_at_cursor(ch_single); - cursor_set_column(cursor_position_to_move); - return; - } - if( _is_pair_right_symbol(ch) && - text[cursor.line][cursor_get_column()] == ch - ) { - cursor_set_column(cursor_position_to_move); - return; - } - } - - - insert_text_at_cursor(ch_pair); - cursor_set_column(cursor_position_to_move); - return; - + + int cursor_position_to_move = cursor_get_column() + 1; + + CharType ch_single[2] = {ch, 0}; + CharType ch_single_pair[2] = {_get_right_pair_symbol(ch), 0}; + CharType ch_pair[3] = {ch, _get_right_pair_symbol(ch), 0}; + + if(is_selection_active()) { + + int new_column,new_line; + + _begin_compex_operation(); + _insert_text(get_selection_from_line(), get_selection_from_column(), + ch_single, + &new_line, &new_column); + + int to_col_offset = 0; + if(get_selection_from_line() == get_selection_to_line()) + to_col_offset = 1; + + _insert_text(get_selection_to_line(), + get_selection_to_column() + to_col_offset, + ch_single_pair, + &new_line,&new_column); + _end_compex_operation(); + + cursor_set_line(get_selection_to_line()); + cursor_set_column(get_selection_to_column() + to_col_offset); + + deselect(); + update(); + return; + } + + if( (ch == '\'' || ch == '"') && + cursor_get_column() > 0 && + _is_text_char(text[cursor.line][cursor_get_column() - 1]) + ) { + insert_text_at_cursor(ch_single); + cursor_set_column(cursor_position_to_move); + return; + } + + if(cursor_get_column() < text[cursor.line].length()) { + if(_is_text_char(text[cursor.line][cursor_get_column()])) { + insert_text_at_cursor(ch_single); + cursor_set_column(cursor_position_to_move); + return; + } + if( _is_pair_right_symbol(ch) && + text[cursor.line][cursor_get_column()] == ch + ) { + cursor_set_column(cursor_position_to_move); + return; + } + } + + + insert_text_at_cursor(ch_pair); + cursor_set_column(cursor_position_to_move); + return; + } void TextEdit::_consume_backspace_for_pair_symbol(int prev_line, int prev_column) { - - bool remove_right_symbol = false; - - if(cursor.column < text[cursor.line].length() && cursor.column > 0) { - - CharType left_char = text[cursor.line][cursor.column - 1]; - CharType right_char = text[cursor.line][cursor.column]; - - if(right_char == _get_right_pair_symbol(left_char)) { - remove_right_symbol = true; - } - - } - if(remove_right_symbol) { - _remove_text(prev_line,prev_column,cursor.line,cursor.column + 1); - } else { - _remove_text(prev_line,prev_column,cursor.line,cursor.column); - } - + + bool remove_right_symbol = false; + + if(cursor.column < text[cursor.line].length() && cursor.column > 0) { + + CharType left_char = text[cursor.line][cursor.column - 1]; + CharType right_char = text[cursor.line][cursor.column]; + + if(right_char == _get_right_pair_symbol(left_char)) { + remove_right_symbol = true; + } + + } + if(remove_right_symbol) { + _remove_text(prev_line,prev_column,cursor.line,cursor.column + 1); + } else { + _remove_text(prev_line,prev_column,cursor.line,cursor.column); + } + } void TextEdit::backspace_at_cursor() { - - if (cursor.column==0 && cursor.line==0) - return; - - int prev_line = cursor.column?cursor.line:cursor.line-1; - int prev_column = cursor.column?(cursor.column-1):(text[cursor.line-1].length()); - if(auto_brace_completion_enabled && - cursor.column > 0 && - _is_pair_left_symbol(text[cursor.line][cursor.column - 1])) { - _consume_backspace_for_pair_symbol(prev_line, prev_column); - } else { - _remove_text(prev_line,prev_column,cursor.line,cursor.column); - } - - cursor_set_line(prev_line); - cursor_set_column(prev_column); - + if (readonly) + return; + + if (cursor.column==0 && cursor.line==0) + return; + + int prev_line = cursor.column?cursor.line:cursor.line-1; + int prev_column = cursor.column?(cursor.column-1):(text[cursor.line-1].length()); + if(auto_brace_completion_enabled && + cursor.column > 0 && + _is_pair_left_symbol(text[cursor.line][cursor.column - 1])) { + _consume_backspace_for_pair_symbol(prev_line, prev_column); + } else { + _remove_text(prev_line,prev_column,cursor.line,cursor.column); + } + + cursor_set_line(prev_line); + cursor_set_column(prev_column); + } -bool TextEdit::_get_mouse_pos(const Point2i& p_mouse, int &r_row, int &r_col) const { - - int row=p_mouse.y; - row-=cache.style_normal->get_margin(MARGIN_TOP); - row/=get_row_height(); - - if (row<0 || row>=get_visible_rows()) - return false; - - row+=cursor.line_ofs; - int col=0; - - if (row>=text.size()) { - - row=text.size()-1; - col=text[row].size(); - } else { +void TextEdit::_get_mouse_pos(const Point2i& p_mouse, int &r_row, int &r_col) const { + + float rows=p_mouse.y; + rows-=cache.style_normal->get_margin(MARGIN_TOP); + rows/=get_row_height(); + int row=cursor.line_ofs+rows; - col=p_mouse.x-(cache.style_normal->get_margin(MARGIN_LEFT)+cache.line_number_w); - col+=cursor.x_ofs; - col=get_char_pos_for( col, get_line(row) ); - } + if (row<0) + row=0; - r_row=row; - r_col=col; - return true; + int col=0; + + if (row>=text.size()) { + + row=text.size()-1; + col=text[row].size(); + } else { + + col=p_mouse.x-(cache.style_normal->get_margin(MARGIN_LEFT)+cache.line_number_w); + col+=cursor.x_ofs; + col=get_char_pos_for( col, get_line(row) ); + } + + r_row=row; + r_col=col; } void TextEdit::_input_event(const InputEvent& p_input_event) { + + switch(p_input_event.type) { + + case InputEvent::MOUSE_BUTTON: { + + const InputEventMouseButton &mb=p_input_event.mouse_button; + + if (completion_active && completion_rect.has_point(Point2(mb.x,mb.y))) { + + if (!mb.pressed) + return; + + if (mb.button_index==BUTTON_WHEEL_UP) { + if (completion_index>0) { + completion_index--; + completion_current=completion_options[completion_index]; + update(); + } + + } + if (mb.button_index==BUTTON_WHEEL_DOWN) { + + if (completion_index<completion_options.size()-1) { + completion_index++; + completion_current=completion_options[completion_index]; + update(); + } + } + + if (mb.button_index==BUTTON_LEFT) { + + completion_index=CLAMP(completion_line_ofs+(mb.y-completion_rect.pos.y)/get_row_height(),0,completion_options.size()-1); + + completion_current=completion_options[completion_index]; + update(); + if (mb.doubleclick) + _confirm_completion(); + } + return; + } else { + _cancel_completion(); + _cancel_code_hint(); + } + + if (mb.pressed) { + if (mb.button_index==BUTTON_WHEEL_UP) { + v_scroll->set_val( v_scroll->get_val() -3 ); + } + if (mb.button_index==BUTTON_WHEEL_DOWN) { + v_scroll->set_val( v_scroll->get_val() +3 ); + } + if (mb.button_index==BUTTON_LEFT) { + + int row,col; + _get_mouse_pos(Point2i(mb.x,mb.y), row,col); + + int prev_col=cursor.column; + int prev_line=cursor.line; + + + + cursor_set_line( row ); + cursor_set_column( col ); + + if (mb.mod.shift && (cursor.column!=prev_col || cursor.line!=prev_line)) { + + if (!selection.active) { + selection.active=true; + selection.selecting_mode=Selection::MODE_POINTER; + selection.from_column=prev_col; + selection.from_line=prev_line; + selection.to_column=cursor.column; + selection.to_line=cursor.line; + + if (selection.from_line>selection.to_line || (selection.from_line==selection.to_line && selection.from_column>selection.to_column)) { + SWAP(selection.from_column,selection.to_column); + SWAP(selection.from_line,selection.to_line); + selection.shiftclick_left=false; + } else { + selection.shiftclick_left=true; + } + selection.selecting_line=prev_line; + selection.selecting_column=prev_col; + update(); + } else { - switch(p_input_event.type) { - - case InputEvent::MOUSE_BUTTON: { - - const InputEventMouseButton &mb=p_input_event.mouse_button; - - if (completion_active && completion_rect.has_point(Point2(mb.x,mb.y))) { - - if (!mb.pressed) - return; - - if (mb.button_index==BUTTON_WHEEL_UP) { - if (completion_index>0) { - completion_index--; - completion_current=completion_options[completion_index]; - update(); - } - - } - if (mb.button_index==BUTTON_WHEEL_DOWN) { - - if (completion_index<completion_options.size()-1) { - completion_index++; - completion_current=completion_options[completion_index]; - update(); - } - } - - if (mb.button_index==BUTTON_LEFT) { - - completion_index=CLAMP(completion_line_ofs+(mb.y-completion_rect.pos.y)/get_row_height(),0,completion_options.size()-1); - - completion_current=completion_options[completion_index]; - update(); - if (mb.doubleclick) - _confirm_completion(); - } - return; - } else { - _cancel_completion(); - _cancel_code_hint(); - } - - if (mb.pressed) { - if (mb.button_index==BUTTON_WHEEL_UP) { - v_scroll->set_val( v_scroll->get_val() -3 ); - } - if (mb.button_index==BUTTON_WHEEL_DOWN) { - v_scroll->set_val( v_scroll->get_val() +3 ); - } - if (mb.button_index==BUTTON_LEFT) { - - int row,col; - if (!_get_mouse_pos(Point2i(mb.x,mb.y), row,col)) - return; - - int prev_col=cursor.column; - int prev_line=cursor.line; - - - - cursor_set_line( row ); - cursor_set_column( col ); - - if (mb.mod.shift && (cursor.column!=prev_col || cursor.line!=prev_line)) { - - selection.active=true; - selection.selecting_mode=Selection::MODE_POINTER; - selection.from_column=prev_col; - selection.from_line=prev_line; - selection.to_column=cursor.column; - selection.to_line=cursor.line; - if (selection.from_column>selection.to_column) { - SWAP(selection.from_column,selection.to_column); - SWAP(selection.from_line,selection.to_line); - } - selection.selecting_line=prev_line; - selection.selecting_column=prev_col; - update(); - - } else { - - //if sel active and dblick last time < something - - //else - selection.active=false; - selection.selecting_mode=Selection::MODE_POINTER; - selection.selecting_line=row; - selection.selecting_column=col; - } - - - if (!mb.doubleclick && (OS::get_singleton()->get_ticks_msec()-last_dblclk)<600) { - //tripleclick select line - select(cursor.line,0,cursor.line,text[cursor.line].length()); - last_dblclk=0; - - } else if (mb.doubleclick && text[cursor.line].length()) { - - //doubleclick select world - String s = text[cursor.line]; - int beg=CLAMP(cursor.column,0,s.length()); - int end=beg; - - if (s[beg]>32 || beg==s.length()) { - - bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this + if (cursor.line<selection.selecting_line || (cursor.line==selection.selecting_line && cursor.column<selection.selecting_column)) { - while(beg>0 && s[beg-1]>32 && (symbol==_is_symbol(s[beg-1]))) { - beg--; - } - while(end<s.length() && s[end+1]>32 && (symbol==_is_symbol(s[end+1]))) { - end++; - } + if (selection.shiftclick_left) { + SWAP(selection.from_column,selection.to_column); + SWAP(selection.from_line,selection.to_line); + selection.shiftclick_left = !selection.shiftclick_left; + } + selection.from_column=cursor.column; + selection.from_line=cursor.line; - if (end<s.length()) - end+=1; + } else if (cursor.line>selection.selecting_line || (cursor.line==selection.selecting_line && cursor.column>selection.selecting_column)) { - select(cursor.line,beg,cursor.line,end); - } + if (!selection.shiftclick_left) { + SWAP(selection.from_column,selection.to_column); + SWAP(selection.from_line,selection.to_line); + selection.shiftclick_left = !selection.shiftclick_left; + } + selection.to_column=cursor.column; + selection.to_line=cursor.line; - last_dblclk = OS::get_singleton()->get_ticks_msec(); + } else { + selection.active=false; + } - } + update(); + } - update(); - } - } else { - selection.selecting_mode=Selection::MODE_NONE; - // notify to show soft keyboard - notification(NOTIFICATION_FOCUS_ENTER); - } - } break; - case InputEvent::MOUSE_MOTION: { - const InputEventMouseMotion &mm=p_input_event.mouse_motion; - if (mm.button_mask&BUTTON_MASK_LEFT) { - int row,col; - if (!_get_mouse_pos(Point2i(mm.x,mm.y), row,col)) - return; + + } else { + + //if sel active and dblick last time < something + + //else + selection.active=false; + selection.selecting_mode=Selection::MODE_POINTER; + selection.selecting_line=row; + selection.selecting_column=col; + } + + + if (!mb.doubleclick && (OS::get_singleton()->get_ticks_msec()-last_dblclk)<600 && cursor.line==prev_line) { + //tripleclick select line + select(cursor.line,0,cursor.line,text[cursor.line].length()); + selection.selecting_column=0; + last_dblclk=0; + + } else if (mb.doubleclick && text[cursor.line].length()) { + + //doubleclick select world + String s = text[cursor.line]; + int beg=CLAMP(cursor.column,0,s.length()); + int end=beg; + + if (s[beg]>32 || beg==s.length()) { + + bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this + + while(beg>0 && s[beg-1]>32 && (symbol==_is_symbol(s[beg-1]))) { + beg--; + } + while(end<s.length() && s[end+1]>32 && (symbol==_is_symbol(s[end+1]))) { + end++; + } + + if (end<s.length()) + end+=1; + + select(cursor.line,beg,cursor.line,end); - if (selection.selecting_mode==Selection::MODE_POINTER) { + selection.selecting_column=beg; + } + + last_dblclk = OS::get_singleton()->get_ticks_msec(); + + } + + update(); + } + } else { + + // notify to show soft keyboard + notification(NOTIFICATION_FOCUS_ENTER); + } + + } break; + case InputEvent::MOUSE_MOTION: { + + const InputEventMouseMotion &mm=p_input_event.mouse_motion; + + if (mm.button_mask&BUTTON_MASK_LEFT) { + + int row,col; + _get_mouse_pos(Point2i(mm.x,mm.y), row,col); + + if (selection.selecting_mode!=Selection::MODE_NONE) { + + select(selection.selecting_line,selection.selecting_column,row,col); + + cursor_set_line( row ); + cursor_set_column( col ); + update(); + + } + + } + + } break; + + case InputEvent::KEY: { + + InputEventKey k=p_input_event.key; + + if (!k.pressed) + return; + + if (completion_active) { + if (readonly) + break; + + bool valid=true; + if (k.mod.command || k.mod.meta) + valid=false; + + if (valid) { + + if (!k.mod.alt) { + if (k.scancode==KEY_UP) { + + if (completion_index>0) { + completion_index--; + completion_current=completion_options[completion_index]; + update(); + } + accept_event(); + return; + } - select(selection.selecting_line,selection.selecting_column,row,col); - cursor_set_line( row ); - cursor_set_column( col ); - update(); + if (k.scancode==KEY_DOWN) { - } + if (completion_index<completion_options.size()-1) { + completion_index++; + completion_current=completion_options[completion_index]; + update(); + } + accept_event(); + return; + } - } + if (k.scancode==KEY_PAGEUP) { - } break; + completion_index-=get_constant("completion_lines"); + if (completion_index<0) + completion_index=0; + completion_current=completion_options[completion_index]; + update(); + accept_event(); + return; + } - case InputEvent::KEY: { - InputEventKey k=p_input_event.key; + if (k.scancode==KEY_PAGEDOWN) { - if (!k.pressed) - return; + completion_index+=get_constant("completion_lines"); + if (completion_index>=completion_options.size()) + completion_index=completion_options.size()-1; + completion_current=completion_options[completion_index]; + update(); + accept_event(); + return; + } - if (completion_active) { + if (k.scancode==KEY_HOME && completion_index>0) { - bool valid=true; - if (k.mod.command || k.mod.alt || k.mod.meta) - valid=false; + completion_index=0; + completion_current=completion_options[completion_index]; + update(); + accept_event(); + return; + } - if (valid) { + if (k.scancode==KEY_END && completion_index<completion_options.size()-1) { - if (k.scancode==KEY_UP) { + completion_index=completion_options.size()-1; + completion_current=completion_options[completion_index]; + update(); + accept_event(); + return; + } - if (completion_index>0) { - completion_index--; - completion_current=completion_options[completion_index]; - update(); - } - accept_event(); - return; - } + if (k.scancode==KEY_DOWN) { - if (k.scancode==KEY_DOWN) { + if (completion_index<completion_options.size()-1) { + completion_index++; + completion_current=completion_options[completion_index]; + update(); + } + accept_event(); + return; + } - if (completion_index<completion_options.size()-1) { - completion_index++; - completion_current=completion_options[completion_index]; - update(); - } - accept_event(); - return; - } + if (k.scancode==KEY_RETURN || k.scancode==KEY_TAB) { - if (k.scancode==KEY_PAGEUP) { + _confirm_completion(); + accept_event(); + return; + } - completion_index-=get_constant("completion_lines"); - if (completion_index<0) - completion_index=0; - completion_current=completion_options[completion_index]; - update(); - accept_event(); - return; - } + if (k.scancode==KEY_BACKSPACE) { + backspace_at_cursor(); + _update_completion_candidates(); + accept_event(); + return; + } - if (k.scancode==KEY_PAGEDOWN) { - completion_index+=get_constant("completion_lines"); - if (completion_index>=completion_options.size()) - completion_index=completion_options.size()-1; - completion_current=completion_options[completion_index]; - update(); - accept_event(); - return; - } + if (k.scancode==KEY_SHIFT) { + accept_event(); + return; + } + } + + if (k.unicode>32) { + + if (cursor.column<text[cursor.line].length() && text[cursor.line][cursor.column]==k.unicode) { + //same char, move ahead + cursor_set_column(cursor.column+1); + + } else { + //different char, go back + const CharType chr[2] = {(CharType)k.unicode, 0}; + if(auto_brace_completion_enabled && _is_pair_symbol(chr[0])) { + _consume_pair_symbol(chr[0]); + } else { + _insert_text_at_cursor(chr); + } + } + + _update_completion_candidates(); + accept_event(); + + return; + } + } + + _cancel_completion(); + + } + + /* TEST CONTROL FIRST!! */ + + // some remaps for duplicate functions.. + if (k.mod.command && !k.mod.shift && !k.mod.alt && !k.mod.meta && k.scancode==KEY_INSERT) { + + k.scancode=KEY_C; + } + if (!k.mod.command && k.mod.shift && !k.mod.alt && !k.mod.meta && k.scancode==KEY_INSERT) { + + k.scancode=KEY_V; + k.mod.command=true; + k.mod.shift=false; + } + + // stuff to do when selection is active.. + + if (selection.active) { + + if (readonly) + break; + + bool clear=false; + bool unselect=false; + bool dobreak=false; + + switch(k.scancode) { + + case KEY_TAB: { + + String txt = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + String prev_txt=txt; + + if (k.mod.shift) { + + for(int i=0;i<txt.length();i++) { + if (((i>0 && txt[i-1]=='\n') || (i==0 /*&& selection.from_column==0*/)) && (txt[i]=='\t' || txt[i]==' ')) { + txt.remove(i); + //i--; + } + } + } else { + + for(int i=0;i<txt.length();i++) { + + if (((i>0 && txt[i-1]=='\n') || (i==0 /*&& selection.from_column==0*/))) { + txt=txt.insert(i,"\t"); + //i--; + } + } + } + + if (txt!=prev_txt) { + + int sel_line=selection.from_line; + int sel_column=selection.from_column; + + cursor_set_line(selection.from_line); + cursor_set_column(selection.from_column); + _begin_compex_operation(); + _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + _insert_text_at_cursor(txt); + _end_compex_operation(); + selection.active=true; + selection.from_column=sel_column; + selection.from_line=sel_line; + selection.to_column=cursor.column; + selection.to_line=cursor.line; + update(); + } + + dobreak=true; + accept_event(); + + } break; + case KEY_X: + case KEY_C: + //special keys often used with control, wait... + clear=(!k.mod.command || k.mod.shift || k.mod.alt ); + break; + case KEY_DELETE: + case KEY_BACKSPACE: + accept_event(); + clear=true; dobreak=true; + break; + case KEY_LEFT: + case KEY_RIGHT: + case KEY_UP: + case KEY_DOWN: + case KEY_PAGEUP: + case KEY_PAGEDOWN: + case KEY_HOME: + case KEY_END: + // ignore arrows if any modifiers are held (shift = selecting, others may be used for editor hotkeys) + if (k.mod.command || k.mod.shift || k.mod.alt) + break; + unselect=true; + break; + + default: + if (k.unicode>=32 && !k.mod.command && !k.mod.alt && !k.mod.meta) + clear=true; + if (auto_brace_completion_enabled && _is_pair_left_symbol(k.unicode)) + clear=false; + } + + if (unselect) { + selection.active=false; + selection.selecting_mode=Selection::MODE_NONE; + update(); + } + if (clear) { + + selection.active=false; + update(); + _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + cursor_set_line(selection.from_line); + cursor_set_column(selection.from_column); + update(); + } + if (dobreak) + break; + } + + selection.selecting_text=false; + + bool scancode_handled=true; + + // special scancode test... + + switch (k.scancode) { + + case KEY_ENTER: + case KEY_RETURN: { + + if (readonly) + break; + + String ins="\n"; + + //keep indentation + for(int i=0;i<text[cursor.line].length();i++) { + if (text[cursor.line][i]=='\t') + ins+="\t"; + else + break; + } + + _insert_text_at_cursor(ins); + _push_current_op(); + + } break; + case KEY_ESCAPE: { + if (completion_hint!="") { + completion_hint=""; + update(); + + } + } break; + case KEY_TAB: { + + if (readonly) + break; + + if (selection.active) { + + + } else { + if (k.mod.shift) { + + int cc = cursor.column; + if (cc>0 && cc<=text[cursor.line].length() && text[cursor.line][cursor.column-1]=='\t') { + //simple unindent + + backspace_at_cursor(); + } + } else { + //simple indent + _insert_text_at_cursor("\t"); + } + } + + } break; + case KEY_BACKSPACE: { + if (readonly) + break; - if (k.scancode==KEY_HOME) { +#ifdef APPLE_STYLE_KEYS + if (k.mod.alt) { +#else + if (k.mod.alt) { + scancode_handled=false; + break; + } else if (k.mod.command) { +#endif + int line=cursor.line; + int column=cursor.column; - completion_index=0; - completion_current=completion_options[completion_index]; - update(); - accept_event(); - return; - } + bool prev_char=false; + bool only_whitespace=true; - if (k.scancode==KEY_END) { + while (only_whitespace && line > 0) { - completion_index=completion_options.size()-1; - completion_current=completion_options[completion_index]; - update(); - accept_event(); - return; - } + while (column>0) { + CharType c=text[line][column-1]; + if (c != '\t' && c != ' ') { + only_whitespace=false; + break; + } - if (k.scancode==KEY_DOWN) { + column--; + } - if (completion_index<completion_options.size()-1) { - completion_index++; - completion_current=completion_options[completion_index]; - update(); - } - accept_event(); - return; - } + if (only_whitespace) { + line--; + column=text[line].length(); + } + } - if (k.scancode==KEY_RETURN || k.scancode==KEY_TAB) { + while (column>0) { + bool ischar=_is_text_char(text[line][column-1]); - _confirm_completion(); - accept_event(); - return; - } + if (prev_char && !ischar) + break; - if (k.scancode==KEY_BACKSPACE) { + prev_char=ischar; + column--; - backspace_at_cursor(); - _update_completion_candidates(); - accept_event(); - return; - } + } + _remove_text(line, column, cursor.line, cursor.column); - if (k.scancode==KEY_SHIFT) { - accept_event(); - return; - } + cursor_set_line(line); + cursor_set_column(column); - if (k.unicode>32) { - - if (cursor.column<text[cursor.line].length() && text[cursor.line][cursor.column]==k.unicode) { - //same char, move ahead - cursor_set_column(cursor.column+1); - - } else { - //different char, go back - const CharType chr[2] = {k.unicode, 0}; - if(auto_brace_completion_enabled && _is_pair_symbol(chr[0])) { - _consume_pair_symbol(chr[0]); - } else { - _insert_text_at_cursor(chr); - } - } - - _update_completion_candidates(); - accept_event(); - - return; - } - } - - _cancel_completion(); - - } - - /* TEST CONTROL FIRST!! */ - - // some remaps for duplicate functions.. - if (k.mod.command && !k.mod.shift && !k.mod.alt && !k.mod.meta && k.scancode==KEY_INSERT) { - - k.scancode=KEY_C; - } - if (!k.mod.command && k.mod.shift && !k.mod.alt && !k.mod.meta && k.scancode==KEY_INSERT) { - - k.scancode=KEY_V; - k.mod.command=true; - k.mod.shift=false; - } - - // stuff to do when selection is active.. - - if (selection.active) { - - bool clear=false; - bool unselect=false; - bool dobreak=false; - - switch(k.scancode) { - - case KEY_TAB: { - - String txt = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - String prev_txt=txt; - - if (k.mod.shift) { - - for(int i=0;i<txt.length();i++) { - if (((i>0 && txt[i-1]=='\n') || (i==0 /*&& selection.from_column==0*/)) && (txt[i]=='\t' || txt[i]==' ')) { - txt.remove(i); - //i--; - } - } - } else { - - for(int i=0;i<txt.length();i++) { - - if (((i>0 && txt[i-1]=='\n') || (i==0 /*&& selection.from_column==0*/))) { - txt=txt.insert(i,"\t"); - //i--; - } - } - } - - if (txt!=prev_txt) { - - int sel_line=selection.from_line; - int sel_column=selection.from_column; - - cursor_set_line(selection.from_line); - cursor_set_column(selection.from_column); - _begin_compex_operation(); - _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - _insert_text_at_cursor(txt); - _end_compex_operation(); - selection.active=true; - selection.from_column=sel_column; - selection.from_line=sel_line; - selection.to_column=cursor.column; - selection.to_line=cursor.line; - update(); - } - - dobreak=true; - accept_event(); - - } break; - case KEY_X: - case KEY_C: - //special keys often used with control, wait... - clear=(!k.mod.command || k.mod.shift || k.mod.alt ); - break; - case KEY_DELETE: - case KEY_BACKSPACE: - accept_event(); - clear=true; dobreak=true; - break; - case KEY_LEFT: - case KEY_RIGHT: - case KEY_UP: - case KEY_DOWN: - case KEY_PAGEUP: - case KEY_PAGEDOWN: - case KEY_HOME: - case KEY_END: - // ignore arrows if any modifiers are held (shift = selecting, others may be used for editor hotkeys) - if (k.mod.command || k.mod.shift || k.mod.alt || k.mod.command) - break; - unselect=true; - break; - - default: - if (k.unicode>=32 && !k.mod.command && !k.mod.alt && !k.mod.meta) - clear=true; - if (auto_brace_completion_enabled && _is_pair_left_symbol(k.unicode)) - clear=false; - } - - if (unselect) { - selection.active=false; - selection.selecting_mode=Selection::MODE_NONE; - update(); - } - if (clear) { - - selection.active=false; - update(); - _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - cursor_set_line(selection.from_line); - cursor_set_column(selection.from_column); - update(); - } - if (dobreak) - break; - } - - selection.selecting_test=false; - - bool scancode_handled=true; - - // special scancode test... - - switch (k.scancode) { - - case KEY_ENTER: - case KEY_RETURN: { - - String ins="\n"; - - //keep indentation - for(int i=0;i<text[cursor.line].length();i++) { - if (text[cursor.line][i]=='\t') - ins+="\t"; - else - break; - } - - _insert_text_at_cursor(ins); - _push_current_op(); - - } break; - case KEY_ESCAPE: { - if (completion_hint!="") { - completion_hint=""; - update(); - - } - } break; - case KEY_TAB: { - - if (readonly) - break; - - if (selection.active) { - - - } else { - if (k.mod.shift) { - - int cc = cursor.column; - if (cc>0 && cc<=text[cursor.line].length() && text[cursor.line][cursor.column-1]=='\t') { - //simple unindent - - backspace_at_cursor(); - } - } else { - //simple indent - _insert_text_at_cursor("\t"); - } - } - - } break; - case KEY_BACKSPACE: { - if (readonly) - break; - backspace_at_cursor(); - - } break; - case KEY_LEFT: { - - if (k.mod.shift) - _pre_shift_selection(); + } else { + backspace_at_cursor(); + } + } break; + case KEY_LEFT: { + + if (k.mod.shift) + _pre_shift_selection(); + #ifdef APPLE_STYLE_KEYS - if (k.mod.command) { - cursor_set_column(0); - } else if (k.mod.alt) { - + if (k.mod.command) { + cursor_set_column(0); + } else if (k.mod.alt) { + #else - if (k.mod.alt) { - scancode_handled=false; - break; - } else if (k.mod.command) { + if (k.mod.alt) { + scancode_handled=false; + break; + } else if (k.mod.command) { #endif - bool prev_char=false; - int cc=cursor.column; - while (cc>0) { - - bool ischar=_is_text_char(text[cursor.line][cc-1]); - - if (prev_char && !ischar) - break; - - prev_char=ischar; - cc--; - - } - - cursor_set_column(cc); - - } else if (cursor.column==0) { - - if (cursor.line>0) { - cursor_set_line(cursor.line-1); - cursor_set_column(text[cursor.line].length()); - } - } else { - cursor_set_column(cursor_get_column()-1); - } - - if (k.mod.shift) - _post_shift_selection(); - - } break; - case KEY_RIGHT: { - - if (k.mod.shift) - _pre_shift_selection(); - + bool prev_char=false; + int cc=cursor.column; + while (cc>0) { + + bool ischar=_is_text_char(text[cursor.line][cc-1]); + + if (prev_char && !ischar) + break; + + prev_char=ischar; + cc--; + + } + + cursor_set_column(cc); + + } else if (cursor.column==0) { + + if (cursor.line>0) { + cursor_set_line(cursor.line-1); + cursor_set_column(text[cursor.line].length()); + } + } else { + cursor_set_column(cursor_get_column()-1); + } + + if (k.mod.shift) + _post_shift_selection(); + + } break; + case KEY_RIGHT: { + + if (k.mod.shift) + _pre_shift_selection(); + #ifdef APPLE_STYLE_KEYS - if (k.mod.command) { - cursor_set_column(text[cursor.line].length()); - } else if (k.mod.alt) { + if (k.mod.command) { + cursor_set_column(text[cursor.line].length()); + } else if (k.mod.alt) { #else - if (k.mod.alt) { - scancode_handled=false; - break; - } else if (k.mod.command) { + if (k.mod.alt) { + scancode_handled=false; + break; + } else if (k.mod.command) { #endif - bool prev_char=false; - int cc=cursor.column; - while (cc<text[cursor.line].length()) { - - bool ischar=_is_text_char(text[cursor.line][cc]); - - if (prev_char && !ischar) - break; - prev_char=ischar; - cc++; - } - - cursor_set_column(cc); - - } else if (cursor.column==text[cursor.line].length()) { - - if (cursor.line<text.size()-1) { - cursor_set_line(cursor.line+1); - cursor_set_column(0); - } - } else { - cursor_set_column(cursor_get_column()+1); - } - - if (k.mod.shift) - _post_shift_selection(); - - } break; - case KEY_UP: { - - if (k.mod.shift) - _pre_shift_selection(); - if (k.mod.alt) { - scancode_handled=false; - break; - } + bool prev_char=false; + int cc=cursor.column; + while (cc<text[cursor.line].length()) { + + bool ischar=_is_text_char(text[cursor.line][cc]); + + if (prev_char && !ischar) + break; + prev_char=ischar; + cc++; + } + + cursor_set_column(cc); + + } else if (cursor.column==text[cursor.line].length()) { + + if (cursor.line<text.size()-1) { + cursor_set_line(cursor.line+1); + cursor_set_column(0); + } + } else { + cursor_set_column(cursor_get_column()+1); + } + + if (k.mod.shift) + _post_shift_selection(); + + } break; + case KEY_UP: { + + if (k.mod.shift) + _pre_shift_selection(); + if (k.mod.alt) { + scancode_handled=false; + break; + } #ifdef APPLE_STYLE_KEYS - if (k.mod.command) - cursor_set_line(0); - else + if (k.mod.command) + cursor_set_line(0); + else #endif - cursor_set_line(cursor_get_line()-1); - - if (k.mod.shift) - _post_shift_selection(); - _cancel_code_hint(); - - } break; - case KEY_DOWN: { - - if (k.mod.shift) - _pre_shift_selection(); - if (k.mod.alt) { - scancode_handled=false; - break; - } + cursor_set_line(cursor_get_line()-1); + + if (k.mod.shift) + _post_shift_selection(); + _cancel_code_hint(); + + } break; + case KEY_DOWN: { + + if (k.mod.shift) + _pre_shift_selection(); + if (k.mod.alt) { + scancode_handled=false; + break; + } #ifdef APPLE_STYLE_KEYS - if (k.mod.command) - cursor_set_line(text.size()-1); - else + if (k.mod.command) + cursor_set_line(text.size()-1); + else #endif - cursor_set_line(cursor_get_line()+1); - - if (k.mod.shift) - _post_shift_selection(); - _cancel_code_hint(); - - } break; - - case KEY_DELETE: { - - if (readonly) - break; - int curline_len = text[cursor.line].length(); + cursor_set_line(cursor_get_line()+1); + + if (k.mod.shift) + _post_shift_selection(); + _cancel_code_hint(); + + } break; + + case KEY_DELETE: { + + if (readonly) + break; + int curline_len = text[cursor.line].length(); + + if (cursor.line==text.size()-1 && cursor.column==curline_len) + break; //nothing to do + + int next_line=cursor.column<curline_len?cursor.line:cursor.line+1; + int next_column; - if (cursor.line==text.size()-1 && cursor.column==curline_len) - break; //nothing to do - - int next_line = cursor.column<curline_len?cursor.line:cursor.line+1; - int next_column = cursor.column<curline_len?(cursor.column+1):0; - _remove_text(cursor.line,cursor.column,next_line,next_column); - update(); - } break; #ifdef APPLE_STYLE_KEYS - case KEY_HOME: { - - - if (k.mod.shift) - _pre_shift_selection(); - - cursor_set_line(0); - - if (k.mod.shift) - _post_shift_selection(); - - } break; - case KEY_END: { - - if (k.mod.shift) - _pre_shift_selection(); - - cursor_set_line(text.size()-1); - - if (k.mod.shift) - _post_shift_selection(); - - } break; - + if (k.mod.alt) { #else - case KEY_HOME: { - - - if (k.mod.shift) - _pre_shift_selection(); - - // compute whitespace symbols seq length - int current_line_whitespace_len = 0; - while(current_line_whitespace_len < text[cursor.line].length()) { - CharType c = text[cursor.line][current_line_whitespace_len]; - if(c != '\t' && c != ' ') - break; - current_line_whitespace_len++; - } - - if(cursor_get_column() == current_line_whitespace_len) - cursor_set_column(0); - else - cursor_set_column(current_line_whitespace_len); - - if (k.mod.command) - cursor_set_line(0); - - if (k.mod.shift) - _post_shift_selection(); - - } break; - case KEY_END: { - - if (k.mod.shift) - _pre_shift_selection(); - - if (k.mod.command) - cursor_set_line(text.size()-1); - cursor_set_column(text[cursor.line].length()); - - if (k.mod.shift) - _post_shift_selection(); - - } break; + if (k.mod.alt) { + scancode_handled=false; + break; + } else if (k.mod.command) { #endif - case KEY_PAGEUP: { - - if (k.mod.shift) - _pre_shift_selection(); - - cursor_set_line(cursor_get_line()-get_visible_rows()); - - if (k.mod.shift) - _post_shift_selection(); - - } break; - case KEY_PAGEDOWN: { - - if (k.mod.shift) - _pre_shift_selection(); - - cursor_set_line(cursor_get_line()+get_visible_rows()); - - if (k.mod.shift) - _post_shift_selection(); - - } break; - case KEY_A: { - - if (!k.mod.command || k.mod.shift || k.mod.alt) { - scancode_handled=false; - break; - } - - if (text.size()==1 && text[0].length()==0) - break; - selection.active=true; - selection.from_line=0; - selection.from_column=0; - selection.to_line=text.size()-1; - selection.to_column=text[selection.to_line].size(); - selection.selecting_mode=Selection::MODE_NONE; - update(); - - } break; - case KEY_X: { - - if (!k.mod.command || k.mod.shift || k.mod.alt) { - scancode_handled=false; - break; - } - - if (!selection.active){ - - String clipboard = text[cursor.line]; - OS::get_singleton()->set_clipboard(clipboard); - cursor_set_line(cursor.line); - cursor_set_column(0); - _remove_text(cursor.line,0,cursor.line,text[cursor.line].length()); - - backspace_at_cursor(); - update(); - cursor_set_line(cursor.line+1); - cut_copy_line = true; - - } - else - { - - String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - OS::get_singleton()->set_clipboard(clipboard); - - cursor_set_line(selection.from_line); - cursor_set_column(selection.from_column); - - _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - selection.active=false; - selection.selecting_mode=Selection::MODE_NONE; - update(); - cut_copy_line = false; - } - - } break; - case KEY_C: { - - if (!k.mod.command || k.mod.shift || k.mod.alt) { - scancode_handled=false; - break; - } - - if (!selection.active){ - String clipboard = _base_get_text(cursor.line,0,cursor.line,text[cursor.line].length()); - OS::get_singleton()->set_clipboard(clipboard); - cut_copy_line = true; - } - else{ - String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - OS::get_singleton()->set_clipboard(clipboard); - cut_copy_line = false; - } - } break; - case KEY_Z: { - - if (!k.mod.command) { - scancode_handled=false; - break; - } - - if (k.mod.shift) - redo(); - else - undo(); - } break; - case KEY_V: { - - if (!k.mod.command || k.mod.shift || k.mod.alt) { - scancode_handled=false; - break; - } - - String clipboard = OS::get_singleton()->get_clipboard(); - - if (selection.active) { - selection.active=false; - _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - cursor_set_line(selection.from_line); - cursor_set_column(selection.from_column); - - } - else if (cut_copy_line) - { - cursor_set_column(0); - String ins="\n"; - clipboard += ins; - } - - _insert_text_at_cursor(clipboard); - - update(); - } break; - case KEY_SPACE: { - if (completion_enabled && k.mod.command) { - - query_code_comple(); - scancode_handled=true; - } else { - scancode_handled=false; - } - - } break; - - case KEY_U:{ - if (!k.mod.command || k.mod.shift || k.mod.alt) { - scancode_handled=false; - break; - } - else { - if (selection.active) { - int ini = selection.from_line; - int end = selection.to_line; - for (int i=ini; i<= end; i++) - { - if (text[i][0] == '#') - _remove_text(i,0,i,1); - } - } - else{ - if (text[cursor.line][0] == '#') - _remove_text(cursor.line,0,cursor.line,1); - } - update(); - } - break;} - - default: { - - scancode_handled=false; - } break; - - } - - if (scancode_handled) - accept_event(); -/* - if (!scancode_handled && !k.mod.command && !k.mod.alt) { - - if (k.unicode>=32) { + int last_line=text.size()-1; - if (readonly) - break; - - accept_event(); - } else { - - break; - } - } -*/ - if (!scancode_handled && !k.mod.command && !k.mod.alt) { //for german kbds + int line=cursor.line; + int column=cursor.column; - if (k.unicode>=32) { + bool prev_char=false; + bool only_whitespace=true; - if (readonly) - break; + while (only_whitespace && line < last_line) { - const CharType chr[2] = {k.unicode, 0}; + while (column<text[line].length()) { + CharType c=text[line][column]; - if(auto_brace_completion_enabled && _is_pair_symbol(chr[0])) { - _consume_pair_symbol(chr[0]); - } else { - _insert_text_at_cursor(chr); - } + if (c != '\t' && c != ' ') { + only_whitespace=false; + break; + } - accept_event(); - } else { + column++; + } - break; - } - } + if (only_whitespace) { + line++; + column=0; + } + } + while (column<text[line].length()) { - if (!selection.selecting_test) { + bool ischar=_is_text_char(text[line][column]); - selection.selecting_mode=Selection::MODE_NONE; - } + if (prev_char && !ischar) + break; + prev_char=ischar; + column++; + } - return; - } break; + next_line=line; + next_column=column; + } else { + next_column=cursor.column<curline_len?(cursor.column+1):0; + } - } + _remove_text(cursor.line,cursor.column,next_line,next_column); + update(); + } break; +#ifdef APPLE_STYLE_KEYS + case KEY_HOME: { + + + if (k.mod.shift) + _pre_shift_selection(); + + cursor_set_line(0); + + if (k.mod.shift) + _post_shift_selection(); + + } break; + case KEY_END: { + + if (k.mod.shift) + _pre_shift_selection(); + + cursor_set_line(text.size()-1); + + if (k.mod.shift) + _post_shift_selection(); + + } break; + +#else + case KEY_HOME: { + + + if (k.mod.shift) + _pre_shift_selection(); + + // compute whitespace symbols seq length + int current_line_whitespace_len = 0; + while(current_line_whitespace_len < text[cursor.line].length()) { + CharType c = text[cursor.line][current_line_whitespace_len]; + if(c != '\t' && c != ' ') + break; + current_line_whitespace_len++; + } + + if(cursor_get_column() == current_line_whitespace_len) + cursor_set_column(0); + else + cursor_set_column(current_line_whitespace_len); + + if (k.mod.command) + cursor_set_line(0); + + if (k.mod.shift) + _post_shift_selection(); + _cancel_completion(); + completion_hint=""; + + } break; + case KEY_END: { + + if (k.mod.shift) + _pre_shift_selection(); + + if (k.mod.command) + cursor_set_line(text.size()-1); + cursor_set_column(text[cursor.line].length()); + + if (k.mod.shift) + _post_shift_selection(); + + _cancel_completion(); + completion_hint=""; + + } break; +#endif + case KEY_PAGEUP: { + + if (k.mod.shift) + _pre_shift_selection(); + + cursor_set_line(cursor_get_line()-get_visible_rows()); + + if (k.mod.shift) + _post_shift_selection(); + + _cancel_completion(); + completion_hint=""; + + + } break; + case KEY_PAGEDOWN: { + + if (k.mod.shift) + _pre_shift_selection(); + + cursor_set_line(cursor_get_line()+get_visible_rows()); + + if (k.mod.shift) + _post_shift_selection(); + + _cancel_completion(); + completion_hint=""; + + + } break; + case KEY_A: { + + if (!k.mod.command || k.mod.shift || k.mod.alt) { + scancode_handled=false; + break; + } + + select_all(); + + } break; + case KEY_X: { + + if (!k.mod.command || k.mod.shift || k.mod.alt) { + scancode_handled=false; + break; + } + + if (!selection.active){ + + String clipboard = text[cursor.line]; + OS::get_singleton()->set_clipboard(clipboard); + cursor_set_line(cursor.line); + cursor_set_column(0); + _remove_text(cursor.line,0,cursor.line,text[cursor.line].length()); + + backspace_at_cursor(); + update(); + cursor_set_line(cursor.line+1); + cut_copy_line = true; + + } + else + { + + String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + OS::get_singleton()->set_clipboard(clipboard); + + cursor_set_line(selection.from_line); + cursor_set_column(selection.from_column); + + _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + selection.active=false; + selection.selecting_mode=Selection::MODE_NONE; + update(); + cut_copy_line = false; + } + + } break; + case KEY_C: { + + if (!k.mod.command || k.mod.shift || k.mod.alt) { + scancode_handled=false; + break; + } + + if (!selection.active){ + String clipboard = _base_get_text(cursor.line,0,cursor.line,text[cursor.line].length()); + OS::get_singleton()->set_clipboard(clipboard); + cut_copy_line = true; + } + else{ + String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + OS::get_singleton()->set_clipboard(clipboard); + cut_copy_line = false; + } + } break; + case KEY_Z: { + + if (!k.mod.command) { + scancode_handled=false; + break; + } + + if (k.mod.shift) + redo(); + else + undo(); + } break; + case KEY_V: { + + if (!k.mod.command || k.mod.shift || k.mod.alt) { + scancode_handled=false; + break; + } + + String clipboard = OS::get_singleton()->get_clipboard(); + + if (selection.active) { + selection.active=false; + _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + cursor_set_line(selection.from_line); + cursor_set_column(selection.from_column); + + } + else if (cut_copy_line) + { + cursor_set_column(0); + String ins="\n"; + clipboard += ins; + } + + _insert_text_at_cursor(clipboard); + + update(); + } break; + case KEY_SPACE: { +#ifdef OSX_ENABLED + if (completion_enabled && k.mod.meta) { //cmd-space is spotlight shortcut in OSX +#else + if (completion_enabled && k.mod.command) { +#endif + + query_code_comple(); + scancode_handled=true; + } else { + scancode_handled=false; + } + + } break; + + case KEY_U:{ + if (!k.mod.command || k.mod.shift) { + scancode_handled=false; + break; + } + else { + if (selection.active) { + int ini = selection.from_line; + int end = selection.to_line; + for (int i=ini; i<= end; i++) + { + if (text[i][0] == '#') + _remove_text(i,0,i,1); + } + } + else{ + if (text[cursor.line][0] == '#') + _remove_text(cursor.line,0,cursor.line,1); + } + update(); + } + break;} + + default: { + + scancode_handled=false; + } break; + + } + + if (scancode_handled) + accept_event(); + /* + if (!scancode_handled && !k.mod.command && !k.mod.alt) { + + if (k.unicode>=32) { + + if (readonly) + break; + + accept_event(); + } else { + + break; + } + } +*/ + if (!scancode_handled && !k.mod.command) { //for german kbds + + if (k.unicode>=32) { + + if (readonly) + break; + + const CharType chr[2] = {(CharType)k.unicode, 0}; + + if (completion_hint!="" && k.unicode==')') { + completion_hint=""; + } + if(auto_brace_completion_enabled && _is_pair_symbol(chr[0])) { + _consume_pair_symbol(chr[0]); + } else { + _insert_text_at_cursor(chr); + } + + accept_event(); + } else { + + break; + } + } + + return; + } break; + + } + } void TextEdit::_pre_shift_selection() { + + + if (!selection.active || selection.selecting_mode==Selection::MODE_NONE) { + + selection.selecting_line=cursor.line; + selection.selecting_column=cursor.column; + selection.active=true; + } - - if (!selection.active || selection.selecting_mode!=Selection::MODE_SHIFT) { - - selection.selecting_line=cursor.line; - selection.selecting_column=cursor.column; - selection.active=true; - selection.selecting_mode=Selection::MODE_SHIFT; - } + selection.selecting_mode=Selection::MODE_SHIFT; } void TextEdit::_post_shift_selection() { - - - if (selection.active && selection.selecting_mode==Selection::MODE_SHIFT) { - - select(selection.selecting_line,selection.selecting_column,cursor.line,cursor.column); - update(); - } - - - selection.selecting_test=true; + + + if (selection.active && selection.selecting_mode==Selection::MODE_SHIFT) { + + select(selection.selecting_line,selection.selecting_column,cursor.line,cursor.column); + update(); + } + + + selection.selecting_text=true; } /**** TEXT EDIT CORE API ****/ void TextEdit::_base_insert_text(int p_line, int p_char,const String& p_text,int &r_end_line,int &r_end_column) { - - //save for undo... - ERR_FAIL_INDEX(p_line,text.size()); - ERR_FAIL_COND(p_char<0); - - /* STEP 1 add spaces if the char is greater than the end of the line */ - while(p_char>text[p_line].length()) { - - text.set(p_line,text[p_line]+String::chr(' ')); - } - - /* STEP 2 separate dest string in pre and post text */ - - String preinsert_text = text[p_line].substr(0,p_char); - String postinsert_text = text[p_line].substr(p_char,text[p_line].size()); - - /* STEP 3 remove \r from source text and separate in substrings */ - - //buh bye \r and split - Vector<String> substrings = p_text.replace("\r","").split("\n"); - - - for(int i=0;i<substrings.size();i++) { - //insert the substrings - - if (i==0) { - - text.set(p_line,preinsert_text+substrings[i]); - } else { - - text.insert(p_line+i,substrings[i]); - } - - if (i==substrings.size()-1){ - - text.set(p_line+i,text[p_line+i]+postinsert_text); - } - } - - r_end_line=p_line+substrings.size()-1; - r_end_column=text[r_end_line].length()-postinsert_text.length(); - - if (!text_changed_dirty && !setting_text) { - if (is_inside_tree()) - MessageQueue::get_singleton()->push_call(this,"_text_changed_emit"); - text_changed_dirty=true; - } - + + //save for undo... + ERR_FAIL_INDEX(p_line,text.size()); + ERR_FAIL_COND(p_char<0); + + /* STEP 1 add spaces if the char is greater than the end of the line */ + while(p_char>text[p_line].length()) { + + text.set(p_line,text[p_line]+String::chr(' ')); + } + + /* STEP 2 separate dest string in pre and post text */ + + String preinsert_text = text[p_line].substr(0,p_char); + String postinsert_text = text[p_line].substr(p_char,text[p_line].size()); + + /* STEP 3 remove \r from source text and separate in substrings */ + + //buh bye \r and split + Vector<String> substrings = p_text.replace("\r","").split("\n"); + + + for(int i=0;i<substrings.size();i++) { + //insert the substrings + + if (i==0) { + + text.set(p_line,preinsert_text+substrings[i]); + } else { + + text.insert(p_line+i,substrings[i]); + } + + if (i==substrings.size()-1){ + + text.set(p_line+i,text[p_line+i]+postinsert_text); + } + } + + r_end_line=p_line+substrings.size()-1; + r_end_column=text[r_end_line].length()-postinsert_text.length(); + + if (!text_changed_dirty && !setting_text) { + if (is_inside_tree()) + MessageQueue::get_singleton()->push_call(this,"_text_changed_emit"); + text_changed_dirty=true; + } + } String TextEdit::_base_get_text(int p_from_line, int p_from_column,int p_to_line,int p_to_column) const { - - ERR_FAIL_INDEX_V(p_from_line,text.size(),String()); - ERR_FAIL_INDEX_V(p_from_column,text[p_from_line].length()+1,String()); - ERR_FAIL_INDEX_V(p_to_line,text.size(),String()); - ERR_FAIL_INDEX_V(p_to_column,text[p_to_line].length()+1,String()); - ERR_FAIL_COND_V(p_to_line < p_from_line ,String()); // from > to - ERR_FAIL_COND_V(p_to_line == p_from_line && p_to_column<p_from_column,String()); // from > to - - String ret; - - for(int i=p_from_line;i<=p_to_line;i++) { - - int begin = (i==p_from_line)?p_from_column:0; - int end = (i==p_to_line)?p_to_column:text[i].length(); - - if (i>p_from_line) - ret+="\n"; - ret+=text[i].substr(begin,end-begin); - } - - return ret; + + ERR_FAIL_INDEX_V(p_from_line,text.size(),String()); + ERR_FAIL_INDEX_V(p_from_column,text[p_from_line].length()+1,String()); + ERR_FAIL_INDEX_V(p_to_line,text.size(),String()); + ERR_FAIL_INDEX_V(p_to_column,text[p_to_line].length()+1,String()); + ERR_FAIL_COND_V(p_to_line < p_from_line ,String()); // from > to + ERR_FAIL_COND_V(p_to_line == p_from_line && p_to_column<p_from_column,String()); // from > to + + String ret; + + for(int i=p_from_line;i<=p_to_line;i++) { + + int begin = (i==p_from_line)?p_from_column:0; + int end = (i==p_to_line)?p_to_column:text[i].length(); + + if (i>p_from_line) + ret+="\n"; + ret+=text[i].substr(begin,end-begin); + } + + return ret; } void TextEdit::_base_remove_text(int p_from_line, int p_from_column,int p_to_line,int p_to_column) { - - ERR_FAIL_INDEX(p_from_line,text.size()); - ERR_FAIL_INDEX(p_from_column,text[p_from_line].length()+1); - ERR_FAIL_INDEX(p_to_line,text.size()); - ERR_FAIL_INDEX(p_to_column,text[p_to_line].length()+1); - ERR_FAIL_COND(p_to_line < p_from_line ); // from > to - ERR_FAIL_COND(p_to_line == p_from_line && p_to_column<p_from_column); // from > to - - - String pre_text = text[p_from_line].substr(0,p_from_column); - String post_text = text[p_to_line].substr(p_to_column,text[p_to_line].length()); - - for(int i=p_from_line;i<p_to_line;i++) { - - text.remove(p_from_line+1); - } - - text.set(p_from_line,pre_text+post_text); - - if (!text_changed_dirty && !setting_text) { - if (is_inside_tree()) - MessageQueue::get_singleton()->push_call(this,"_text_changed_emit"); - text_changed_dirty=true; - } + + ERR_FAIL_INDEX(p_from_line,text.size()); + ERR_FAIL_INDEX(p_from_column,text[p_from_line].length()+1); + ERR_FAIL_INDEX(p_to_line,text.size()); + ERR_FAIL_INDEX(p_to_column,text[p_to_line].length()+1); + ERR_FAIL_COND(p_to_line < p_from_line ); // from > to + ERR_FAIL_COND(p_to_line == p_from_line && p_to_column<p_from_column); // from > to + + + String pre_text = text[p_from_line].substr(0,p_from_column); + String post_text = text[p_to_line].substr(p_to_column,text[p_to_line].length()); + + for(int i=p_from_line;i<p_to_line;i++) { + + text.remove(p_from_line+1); + } + + text.set(p_from_line,pre_text+post_text); + + if (!text_changed_dirty && !setting_text) { + if (is_inside_tree()) + MessageQueue::get_singleton()->push_call(this,"_text_changed_emit"); + text_changed_dirty=true; + } } void TextEdit::_insert_text(int p_line, int p_char,const String& p_text,int *r_end_line,int *r_end_column) { - - if (!setting_text) - idle_detect->start(); - - if (undo_enabled) { - _clear_redo(); - } - - int retline,retchar; - _base_insert_text(p_line,p_char,p_text,retline,retchar); - if (r_end_line) - *r_end_line=retline; - if (r_end_column) - *r_end_column=retchar; - - if (!undo_enabled) - return; - - /* UNDO!! */ - TextOperation op; - op.type=TextOperation::TYPE_INSERT; - op.from_line=p_line; - op.from_column=p_char; - op.to_line=retline; - op.to_column=retchar; - op.text=p_text; - op.version=++version; - op.chain_forward=false; - op.chain_backward=false; - - //see if it shold just be set as current op - if (current_op.type!=op.type) { - _push_current_op(); - current_op=op; - - return; //set as current op, return - } - //see if it can be merged - if (current_op.to_line!=p_line || current_op.to_column!=p_char) { - _push_current_op(); - current_op=op; - return; //set as current op, return - } - //merge current op - - current_op.text+=p_text; - current_op.to_column=retchar; - current_op.to_line=retline; - current_op.version=op.version; - + + if (!setting_text) + idle_detect->start(); + + if (undo_enabled) { + _clear_redo(); + } + + int retline,retchar; + _base_insert_text(p_line,p_char,p_text,retline,retchar); + if (r_end_line) + *r_end_line=retline; + if (r_end_column) + *r_end_column=retchar; + + if (!undo_enabled) + return; + + /* UNDO!! */ + TextOperation op; + op.type=TextOperation::TYPE_INSERT; + op.from_line=p_line; + op.from_column=p_char; + op.to_line=retline; + op.to_column=retchar; + op.text=p_text; + op.version=++version; + op.chain_forward=false; + op.chain_backward=false; + + //see if it shold just be set as current op + if (current_op.type!=op.type) { + _push_current_op(); + current_op=op; + + return; //set as current op, return + } + //see if it can be merged + if (current_op.to_line!=p_line || current_op.to_column!=p_char) { + _push_current_op(); + current_op=op; + return; //set as current op, return + } + //merge current op + + current_op.text+=p_text; + current_op.to_column=retchar; + current_op.to_line=retline; + current_op.version=op.version; + } void TextEdit::_remove_text(int p_from_line, int p_from_column,int p_to_line,int p_to_column) { - - if (!setting_text) - idle_detect->start(); - - String text; - if (undo_enabled) { - _clear_redo(); - text=_base_get_text(p_from_line,p_from_column,p_to_line,p_to_column); - } - - _base_remove_text(p_from_line,p_from_column,p_to_line,p_to_column); - - if (!undo_enabled) - return; - - /* UNDO!! */ - TextOperation op; - op.type=TextOperation::TYPE_REMOVE; - op.from_line=p_from_line; - op.from_column=p_from_column; - op.to_line=p_to_line; - op.to_column=p_to_column; - op.text=text; - op.version=++version; - op.chain_forward=false; - op.chain_backward=false; - - //see if it shold just be set as current op - if (current_op.type!=op.type) { - _push_current_op(); - current_op=op; - return; //set as current op, return - } - //see if it can be merged - if (current_op.from_line==p_to_line && current_op.from_column==p_to_column) { - //basckace or similar - current_op.text=text+current_op.text; - current_op.from_line=p_from_line; - current_op.from_column=p_from_column; - return; //update current op - } - if (current_op.from_line==p_from_line && current_op.from_column==p_from_column) { - - //current_op.text=text+current_op.text; - //current_op.from_line=p_from_line; - //current_op.from_column=p_from_column; - //return; //update current op - } - - _push_current_op(); - current_op=op; - + + if (!setting_text) + idle_detect->start(); + + String text; + if (undo_enabled) { + _clear_redo(); + text=_base_get_text(p_from_line,p_from_column,p_to_line,p_to_column); + } + + _base_remove_text(p_from_line,p_from_column,p_to_line,p_to_column); + + if (!undo_enabled) + return; + + /* UNDO!! */ + TextOperation op; + op.type=TextOperation::TYPE_REMOVE; + op.from_line=p_from_line; + op.from_column=p_from_column; + op.to_line=p_to_line; + op.to_column=p_to_column; + op.text=text; + op.version=++version; + op.chain_forward=false; + op.chain_backward=false; + + //see if it shold just be set as current op + if (current_op.type!=op.type) { + _push_current_op(); + current_op=op; + return; //set as current op, return + } + //see if it can be merged + if (current_op.from_line==p_to_line && current_op.from_column==p_to_column) { + //basckace or similar + current_op.text=text+current_op.text; + current_op.from_line=p_from_line; + current_op.from_column=p_from_column; + return; //update current op + } + if (current_op.from_line==p_from_line && current_op.from_column==p_from_column) { + + //current_op.text=text+current_op.text; + //current_op.from_line=p_from_line; + //current_op.from_column=p_from_column; + //return; //update current op + } + + _push_current_op(); + current_op=op; + } void TextEdit::_insert_text_at_cursor(const String& p_text) { - - int new_column,new_line; - _insert_text(cursor.line,cursor.column,p_text,&new_line,&new_column); - cursor_set_line(new_line); - cursor_set_column(new_column); - - update(); + + int new_column,new_line; + _insert_text(cursor.line,cursor.column,p_text,&new_line,&new_column); + cursor_set_line(new_line); + cursor_set_column(new_column); + + update(); } int TextEdit::get_char_count() { - - int totalsize=0; - - for (int i=0;i<text.size();i++) { - - if (i>0) - totalsize++; // incliude \n - totalsize+=text[i].length(); - } - - return totalsize; // omit last \n + + int totalsize=0; + + for (int i=0;i<text.size();i++) { + + if (i>0) + totalsize++; // incliude \n + totalsize+=text[i].length(); + } + + return totalsize; // omit last \n } Size2 TextEdit::get_minimum_size() { - - return cache.style_normal->get_minimum_size(); + + return cache.style_normal->get_minimum_size(); } int TextEdit::get_visible_rows() const { - - int total=cache.size.height; - total-=cache.style_normal->get_minimum_size().height; - total/=get_row_height(); - return total; + + int total=cache.size.height; + total-=cache.style_normal->get_minimum_size().height; + total/=get_row_height(); + return total; } void TextEdit::adjust_viewport_to_cursor() { - - if (cursor.line_ofs>cursor.line) - cursor.line_ofs=cursor.line; - - int visible_width=cache.size.width-cache.style_normal->get_minimum_size().width-cache.line_number_w; - if (v_scroll->is_visible()) - visible_width-=v_scroll->get_combined_minimum_size().width; - visible_width-=20; // give it a little more space - - - //printf("rowofs %i, visrows %i, cursor.line %i\n",cursor.line_ofs,get_visible_rows(),cursor.line); - - int visible_rows = get_visible_rows(); - if (h_scroll->is_visible()) - visible_rows-=((h_scroll->get_combined_minimum_size().height-1)/get_row_height()); - - if (cursor.line>=(cursor.line_ofs+visible_rows)) - cursor.line_ofs=cursor.line-visible_rows+1; - if (cursor.line<cursor.line_ofs) - cursor.line_ofs=cursor.line; - - int cursor_x = get_column_x_offset( cursor.column, text[cursor.line] ); - - if (cursor_x>(cursor.x_ofs+visible_width)) - cursor.x_ofs=cursor_x-visible_width+1; - - if (cursor_x < cursor.x_ofs) - cursor.x_ofs=cursor_x; - - update(); -/* + + if (cursor.line_ofs>cursor.line) + cursor.line_ofs=cursor.line; + + int visible_width=cache.size.width-cache.style_normal->get_minimum_size().width-cache.line_number_w; + if (v_scroll->is_visible()) + visible_width-=v_scroll->get_combined_minimum_size().width; + visible_width-=20; // give it a little more space + + + //printf("rowofs %i, visrows %i, cursor.line %i\n",cursor.line_ofs,get_visible_rows(),cursor.line); + + int visible_rows = get_visible_rows(); + if (h_scroll->is_visible()) + visible_rows-=((h_scroll->get_combined_minimum_size().height-1)/get_row_height()); + + if (cursor.line>=(cursor.line_ofs+visible_rows)) + cursor.line_ofs=cursor.line-visible_rows+1; + if (cursor.line<cursor.line_ofs) + cursor.line_ofs=cursor.line; + + int cursor_x = get_column_x_offset( cursor.column, text[cursor.line] ); + + if (cursor_x>(cursor.x_ofs+visible_width)) + cursor.x_ofs=cursor_x-visible_width+1; + + if (cursor_x < cursor.x_ofs) + cursor.x_ofs=cursor_x; + + update(); + /* get_range()->set_max(text.size()); - + get_range()->set_page(get_visible_rows()); - + get_range()->set((int)cursor.line_ofs); */ - - -} - -void TextEdit::cursor_set_column(int p_col) { - - if (p_col<0) - p_col=0; - - cursor.column=p_col; - if (cursor.column > get_line( cursor.line ).length()) - cursor.column=get_line( cursor.line ).length(); - - cursor.last_fit_x=get_column_x_offset(cursor.column,get_line(cursor.line)); - - adjust_viewport_to_cursor(); - - if (!cursor_changed_dirty) { - if (is_inside_tree()) - MessageQueue::get_singleton()->push_call(this,"_cursor_changed_emit"); - cursor_changed_dirty=true; - } - -} - - -void TextEdit::cursor_set_line(int p_row) { - - if (setting_row) - return; - - setting_row=true; - if (p_row<0) - p_row=0; - - - if (p_row>=(int)text.size()) - p_row=(int)text.size()-1; - - cursor.line=p_row; - cursor.column=get_char_pos_for( cursor.last_fit_x, get_line( cursor.line) ); - - - adjust_viewport_to_cursor(); - - setting_row=false; - - - if (!cursor_changed_dirty) { - if (is_inside_tree()) - MessageQueue::get_singleton()->push_call(this,"_cursor_changed_emit"); - cursor_changed_dirty=true; - } - + + +} + +void TextEdit::cursor_set_column(int p_col, bool p_adjust_viewport) { + + if (p_col<0) + p_col=0; + + cursor.column=p_col; + if (cursor.column > get_line( cursor.line ).length()) + cursor.column=get_line( cursor.line ).length(); + + cursor.last_fit_x=get_column_x_offset(cursor.column,get_line(cursor.line)); + + if (p_adjust_viewport) + adjust_viewport_to_cursor(); + + if (!cursor_changed_dirty) { + if (is_inside_tree()) + MessageQueue::get_singleton()->push_call(this,"_cursor_changed_emit"); + cursor_changed_dirty=true; + } + +} + + +void TextEdit::cursor_set_line(int p_row, bool p_adjust_viewport) { + + if (setting_row) + return; + + setting_row=true; + if (p_row<0) + p_row=0; + + + if (p_row>=(int)text.size()) + p_row=(int)text.size()-1; + + cursor.line=p_row; + cursor.column=get_char_pos_for( cursor.last_fit_x, get_line( cursor.line) ); + + if (p_adjust_viewport) + adjust_viewport_to_cursor(); + + setting_row=false; + + + if (!cursor_changed_dirty) { + if (is_inside_tree()) + MessageQueue::get_singleton()->push_call(this,"_cursor_changed_emit"); + cursor_changed_dirty=true; + } + } int TextEdit::cursor_get_column() const { - - return cursor.column; + + return cursor.column; } int TextEdit::cursor_get_line() const { - - return cursor.line; + + return cursor.line; } void TextEdit::_scroll_moved(double p_to_val) { - - if (updating_scrolls) - return; - - if (h_scroll->is_visible()) - cursor.x_ofs=h_scroll->get_val(); - if (v_scroll->is_visible()) - cursor.line_ofs=v_scroll->get_val(); - update(); + + if (updating_scrolls) + return; + + if (h_scroll->is_visible()) + cursor.x_ofs=h_scroll->get_val(); + if (v_scroll->is_visible()) + cursor.line_ofs=v_scroll->get_val(); + update(); } @@ -2395,798 +2606,816 @@ void TextEdit::_scroll_moved(double p_to_val) { int TextEdit::get_row_height() const { - - return cache.font->get_height()+cache.line_spacing; + + return cache.font->get_height()+cache.line_spacing; } int TextEdit::get_char_pos_for(int p_px,String p_str) const { - - int px=0; - int c=0; - - int tab_w = cache.font->get_char_size(' ').width*tab_size; - - while (c<p_str.length()) { - - int w=0; - - if (p_str[c]=='\t') { - - int left = px%tab_w; - if (left==0) - w=tab_w; - else - w=tab_w-px%tab_w; // is right... - - } else { - - w=cache.font->get_char_size(p_str[c],p_str[c+1]).width; - } - - if (p_px<(px+w/2)) - break; - px+=w; - c++; - } - - return c; + + int px=0; + int c=0; + + int tab_w = cache.font->get_char_size(' ').width*tab_size; + + while (c<p_str.length()) { + + int w=0; + + if (p_str[c]=='\t') { + + int left = px%tab_w; + if (left==0) + w=tab_w; + else + w=tab_w-px%tab_w; // is right... + + } else { + + w=cache.font->get_char_size(p_str[c],p_str[c+1]).width; + } + + if (p_px<(px+w/2)) + break; + px+=w; + c++; + } + + return c; } int TextEdit::get_column_x_offset(int p_char,String p_str) { - - int px=0; - - int tab_w = cache.font->get_char_size(' ').width*tab_size; - - for (int i=0;i<p_char;i++) { - - if (i>=p_str.length()) - break; - - if (p_str[i]=='\t') { - - int left = px%tab_w; - if (left==0) - px+=tab_w; - else - px+=tab_w-px%tab_w; // is right... - - } else { - px+=cache.font->get_char_size(p_str[i],p_str[i+1]).width; - } - } - - return px; - + + int px=0; + + int tab_w = cache.font->get_char_size(' ').width*tab_size; + + for (int i=0;i<p_char;i++) { + + if (i>=p_str.length()) + break; + + if (p_str[i]=='\t') { + + int left = px%tab_w; + if (left==0) + px+=tab_w; + else + px+=tab_w-px%tab_w; // is right... + + } else { + px+=cache.font->get_char_size(p_str[i],p_str[i+1]).width; + } + } + + return px; + } void TextEdit::insert_text_at_cursor(const String& p_text) { - - if (selection.active) { - - cursor_set_line(selection.from_line); - cursor_set_column(selection.from_column); - - _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - selection.active=false; - selection.selecting_mode=Selection::MODE_NONE; - - } - - _insert_text_at_cursor(p_text); - update(); - + + if (selection.active) { + + cursor_set_line(selection.from_line); + cursor_set_column(selection.from_column); + + _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + selection.active=false; + selection.selecting_mode=Selection::MODE_NONE; + + } + + _insert_text_at_cursor(p_text); + update(); + } Control::CursorShape TextEdit::get_cursor_shape(const Point2& p_pos) const { - if(completion_active && completion_rect.has_point(p_pos)) { - return CURSOR_ARROW; - } - return CURSOR_IBEAM; + if(completion_active && completion_rect.has_point(p_pos)) { + return CURSOR_ARROW; + } + return CURSOR_IBEAM; } void TextEdit::set_text(String p_text){ - - setting_text=true; - _clear(); - _insert_text_at_cursor(p_text); - - cursor.column=0; - cursor.line=0; - cursor.x_ofs=0; - cursor.line_ofs=0; - cursor.last_fit_x=0; - cursor_set_line(0); - cursor_set_column(0); - update(); - setting_text=false; - - //get_range()->set(0); + + setting_text=true; + clear(); + _insert_text_at_cursor(p_text); + clear_undo_history(); + cursor.column=0; + cursor.line=0; + cursor.x_ofs=0; + cursor.line_ofs=0; + cursor.last_fit_x=0; + cursor_set_line(0); + cursor_set_column(0); + update(); + setting_text=false; + + //get_range()->set(0); }; String TextEdit::get_text() { - String longthing; - int len = text.size(); - for (int i=0;i<len;i++) { - - - longthing+=text[i]; - if (i!=len-1) - longthing+="\n"; - } - - return longthing; - + String longthing; + int len = text.size(); + for (int i=0;i<len;i++) { + + + longthing+=text[i]; + if (i!=len-1) + longthing+="\n"; + } + + return longthing; + }; String TextEdit::get_text_for_completion() { - - String longthing; - int len = text.size(); - for (int i=0;i<len;i++) { - - if (i==cursor.line) { - longthing+=text[i].substr(0,cursor.column); - longthing+=String::chr(0xFFFF); //not unicode, represents the cursor - longthing+=text[i].substr(cursor.column,text[i].size()); - } else { - - longthing+=text[i]; + + String longthing; + int len = text.size(); + for (int i=0;i<len;i++) { + + if (i==cursor.line) { + longthing+=text[i].substr(0,cursor.column); + longthing+=String::chr(0xFFFF); //not unicode, represents the cursor + longthing+=text[i].substr(cursor.column,text[i].size()); + } else { + + longthing+=text[i]; + } + + + if (i!=len-1) + longthing+="\n"; } - - - if (i!=len-1) - longthing+="\n"; - } - - return longthing; - + + return longthing; + }; String TextEdit::get_line(int line) const { - - if (line<0 || line>=text.size()) - return ""; - - return text[line]; - + + if (line<0 || line>=text.size()) + return ""; + + return text[line]; + }; void TextEdit::_clear() { - - clear_undo_history(); - text.clear(); - cursor.column=0; - cursor.line=0; - cursor.x_ofs=0; - cursor.line_ofs=0; - cursor.last_fit_x=0; + + clear_undo_history(); + text.clear(); + cursor.column=0; + cursor.line=0; + cursor.x_ofs=0; + cursor.line_ofs=0; + cursor.last_fit_x=0; } void TextEdit::clear() { - - setting_text=true; - _clear(); - setting_text=false; - + + setting_text=true; + _clear(); + setting_text=false; + }; void TextEdit::set_readonly(bool p_readonly) { - - - readonly=p_readonly; + + + readonly=p_readonly; } void TextEdit::set_wrap(bool p_wrap) { - - wrap=p_wrap; + + wrap=p_wrap; } void TextEdit::set_max_chars(int p_max_chars) { - - max_chars=p_max_chars; + + max_chars=p_max_chars; } void TextEdit::_update_caches() { - - cache.style_normal=get_stylebox("normal"); - cache.style_focus=get_stylebox("focus"); - cache.font=get_font("font"); - cache.font_color=get_color("font_color"); - cache.font_selected_color=get_color("font_selected_color"); - cache.keyword_color=get_color("keyword_color"); - cache.selection_color=get_color("selection_color"); - cache.mark_color=get_color("mark_color"); - cache.current_line_color=get_color("current_line_color"); - cache.breakpoint_color=get_color("breakpoint_color"); - cache.brace_mismatch_color=get_color("brace_mismatch_color"); - cache.line_spacing=get_constant("line_spacing"); - cache.row_height = cache.font->get_height() + cache.line_spacing; - cache.tab_icon=get_icon("tab"); - text.set_font(cache.font); - + + cache.style_normal=get_stylebox("normal"); + cache.style_focus=get_stylebox("focus"); + cache.font=get_font("font"); + cache.font_color=get_color("font_color"); + cache.font_selected_color=get_color("font_selected_color"); + cache.keyword_color=get_color("keyword_color"); + cache.selection_color=get_color("selection_color"); + cache.mark_color=get_color("mark_color"); + cache.current_line_color=get_color("current_line_color"); + cache.breakpoint_color=get_color("breakpoint_color"); + cache.brace_mismatch_color=get_color("brace_mismatch_color"); + cache.line_spacing=get_constant("line_spacing"); + cache.row_height = cache.font->get_height() + cache.line_spacing; + cache.tab_icon=get_icon("tab"); + text.set_font(cache.font); + } void TextEdit::clear_colors() { - - keywords.clear(); - color_regions.clear();; - text.clear_caches(); - custom_bg_color=Color(0,0,0,0); + + keywords.clear(); + color_regions.clear();; + text.clear_caches(); + custom_bg_color=Color(0,0,0,0); } void TextEdit::set_custom_bg_color(const Color& p_color) { - - custom_bg_color=p_color; - update(); + + custom_bg_color=p_color; + update(); } void TextEdit::add_keyword_color(const String& p_keyword,const Color& p_color) { - - keywords[p_keyword]=p_color; - update(); - + + keywords[p_keyword]=p_color; + update(); + } void TextEdit::add_color_region(const String& p_begin_key,const String& p_end_key,const Color &p_color,bool p_line_only) { - - color_regions.push_back(ColorRegion(p_begin_key,p_end_key,p_color,p_line_only)); - text.clear_caches(); - update(); - + + color_regions.push_back(ColorRegion(p_begin_key,p_end_key,p_color,p_line_only)); + text.clear_caches(); + update(); + } void TextEdit::set_symbol_color(const Color& p_color) { - - symbol_color=p_color; - update(); + + symbol_color=p_color; + update(); } void TextEdit::set_syntax_coloring(bool p_enabled) { - - syntax_coloring=p_enabled; - update(); + + syntax_coloring=p_enabled; + update(); } bool TextEdit::is_syntax_coloring_enabled() const { - - return syntax_coloring; + + return syntax_coloring; } void TextEdit::cut() { - - if (!selection.active) - return; - - String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - OS::get_singleton()->set_clipboard(clipboard); - - cursor_set_line(selection.from_line); - cursor_set_column(selection.from_column); - - _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - selection.active=false; - selection.selecting_mode=Selection::MODE_NONE; - update(); - + + if (!selection.active) + return; + + String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + OS::get_singleton()->set_clipboard(clipboard); + + cursor_set_line(selection.from_line); + cursor_set_column(selection.from_column); + + _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + selection.active=false; + selection.selecting_mode=Selection::MODE_NONE; + update(); + } void TextEdit::copy() { - - if (!selection.active) - return; - - String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - OS::get_singleton()->set_clipboard(clipboard); - + + if (!selection.active) + return; + + print_line("from line: "+itos(selection.from_line)); + print_line("from column: "+itos(selection.from_column)); + print_line("to line: "+itos(selection.to_line)); + print_line("to column: "+itos(selection.to_column)); + + String clipboard = _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + OS::get_singleton()->set_clipboard(clipboard); + } void TextEdit::paste() { - - if (selection.active) { - - cursor_set_line(selection.from_line); - cursor_set_column(selection.from_column); - - _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - selection.active=false; - selection.selecting_mode=Selection::MODE_NONE; - - } - - String clipboard = OS::get_singleton()->get_clipboard(); - _insert_text_at_cursor(clipboard); - update(); - + + if (selection.active) { + + cursor_set_line(selection.from_line); + cursor_set_column(selection.from_column); + + _remove_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + selection.active=false; + selection.selecting_mode=Selection::MODE_NONE; + + } + + String clipboard = OS::get_singleton()->get_clipboard(); + _insert_text_at_cursor(clipboard); + update(); + } void TextEdit::select_all() { - - if (text.size()==1 && text[0].length()==0) - return; - selection.active=true; - selection.from_line=0; - selection.from_column=0; - selection.to_line=text.size()-1; - selection.to_column=text[selection.to_line].size(); - selection.selecting_mode=Selection::MODE_NONE; - update(); - + + if (text.size()==1 && text[0].length()==0) + return; + selection.active=true; + selection.from_line=0; + selection.from_column=0; + selection.selecting_line=0; + selection.selecting_column=0; + selection.to_line=text.size()-1; + selection.to_column=text[selection.to_line].length(); + selection.selecting_mode=Selection::MODE_SHIFT; + selection.shiftclick_left=true; + cursor_set_line( selection.to_line, false ); + cursor_set_column( selection.to_column, false ); + update(); + } void TextEdit::deselect() { - - selection.active=false; - update(); + + selection.active=false; + update(); } void TextEdit::select(int p_from_line,int p_from_column,int p_to_line,int p_to_column) { + + if (p_from_line>=text.size()) + p_from_line=text.size()-1; + if (p_from_column>=text[p_from_line].length()) + p_from_column=text[p_from_line].length(); + + if (p_to_line>=text.size()) + p_to_line=text.size()-1; + if (p_to_column>=text[p_to_line].length()) + p_to_column=text[p_to_line].length(); + + selection.from_line=p_from_line; + selection.from_column=p_from_column; + selection.to_line=p_to_line; + selection.to_column=p_to_column; + + selection.active=true; + + if (selection.from_line==selection.to_line) { + + if (selection.from_column==selection.to_column) { + + selection.active=false; + + } else if (selection.from_column>selection.to_column) { + + selection.shiftclick_left = false; + SWAP( selection.from_column, selection.to_column ); + } else { + + selection.shiftclick_left = true; + } + } else if (selection.from_line>selection.to_line) { + + selection.shiftclick_left = false; + SWAP( selection.from_line, selection.to_line ); + SWAP( selection.from_column, selection.to_column ); + } else { - if (p_from_line>=text.size()) - p_from_line=text.size()-1; - if (p_from_column>=text[p_from_line].length()) - p_from_column=text[p_from_line].length(); - - if (p_to_line>=text.size()) - p_to_line=text.size()-1; - if (p_to_column>=text[p_to_line].length()) - p_to_column=text[p_to_line].length(); - - selection.from_line=p_from_line; - selection.from_column=p_from_column; - selection.to_line=p_to_line; - selection.to_column=p_to_column; - - selection.active=true; - - if (selection.from_line==selection.to_line) { - - if (selection.from_column==selection.to_column) { - - selection.active=false; - - } else if (selection.from_column>selection.to_column) { - - SWAP( selection.from_column, selection.to_column ); - } - } else if (selection.from_line>selection.to_line) { - - SWAP( selection.from_line, selection.to_line ); - SWAP( selection.from_column, selection.to_column ); - } - - - update(); + selection.shiftclick_left = true; + } + + + update(); } bool TextEdit::is_selection_active() const { - - return selection.active; + + return selection.active; } int TextEdit::get_selection_from_line() const { - - ERR_FAIL_COND_V(!selection.active,-1); - return selection.from_line; - + + ERR_FAIL_COND_V(!selection.active,-1); + return selection.from_line; + } int TextEdit::get_selection_from_column() const { - - ERR_FAIL_COND_V(!selection.active,-1); - return selection.from_column; - + + ERR_FAIL_COND_V(!selection.active,-1); + return selection.from_column; + } int TextEdit::get_selection_to_line() const { - - ERR_FAIL_COND_V(!selection.active,-1); - return selection.to_line; - + + ERR_FAIL_COND_V(!selection.active,-1); + return selection.to_line; + } int TextEdit::get_selection_to_column() const { - - ERR_FAIL_COND_V(!selection.active,-1); - return selection.to_column; - + + ERR_FAIL_COND_V(!selection.active,-1); + return selection.to_column; + } String TextEdit::get_selection_text() const { - - if (!selection.active) - return ""; - - return _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); - + + if (!selection.active) + return ""; + + return _base_get_text(selection.from_line,selection.from_column,selection.to_line,selection.to_column); + } String TextEdit::get_word_under_cursor() const { - - int prev_cc = cursor.column; - while(prev_cc >0) { - bool is_char = _is_text_char(text[cursor.line][prev_cc-1]); - if (!is_char) - break; - --prev_cc; - } - - int next_cc = cursor.column; - while(next_cc<text[cursor.line].length()) { - bool is_char = _is_text_char(text[cursor.line][next_cc]); - if(!is_char) - break; - ++ next_cc; - } - if (prev_cc == cursor.column || next_cc == cursor.column) - return ""; - return text[cursor.line].substr(prev_cc, next_cc-prev_cc); + + int prev_cc = cursor.column; + while(prev_cc >0) { + bool is_char = _is_text_char(text[cursor.line][prev_cc-1]); + if (!is_char) + break; + --prev_cc; + } + + int next_cc = cursor.column; + while(next_cc<text[cursor.line].length()) { + bool is_char = _is_text_char(text[cursor.line][next_cc]); + if(!is_char) + break; + ++ next_cc; + } + if (prev_cc == cursor.column || next_cc == cursor.column) + return ""; + return text[cursor.line].substr(prev_cc, next_cc-prev_cc); } DVector<int> TextEdit::_search_bind(const String &p_key,uint32_t p_search_flags, int p_from_line,int p_from_column) const { - - int col,line; - if (search(p_key,p_search_flags,p_from_line,p_from_column,col,line)) { - DVector<int> result; - result.resize(2); - result.set(0,line); - result.set(1,col); - return result; - - } else { - - return DVector<int>(); - } + + int col,line; + if (search(p_key,p_search_flags,p_from_line,p_from_column,col,line)) { + DVector<int> result; + result.resize(2); + result.set(0,line); + result.set(1,col); + return result; + + } else { + + return DVector<int>(); + } } bool TextEdit::search(const String &p_key,uint32_t p_search_flags, int p_from_line, int p_from_column,int &r_line,int &r_column) const { - - if (p_key.length()==0) - return false; - ERR_FAIL_INDEX_V(p_from_line,text.size(),false); - ERR_FAIL_INDEX_V(p_from_column,text[p_from_line].length()+1,false); - - //search through the whole documment, but start by current line - - int line=-1; - int pos=-1; - - line=p_from_line; - - for(int i=0;i<text.size()+1;i++) { - //backwards is broken... - //int idx=(p_search_flags&SEARCH_BACKWARDS)?(text.size()-i):i; //do backwards seearch - - - if (line<0) { - line=text.size()-1; - } - if (line==text.size()) { - line=0; - } - - String text_line = text[line]; - int from_column=0; - if (line==p_from_line) { - - if (i==text.size()) { - //wrapped - - if (p_search_flags&SEARCH_BACKWARDS) { - text_line=text_line.substr(from_column,text_line.length()); - from_column=text_line.length(); - } else { - text_line=text_line.substr(0,from_column); - from_column=0; - } - - } else { - - from_column=p_from_column; - } - - - } else { - //text_line=text_line.substr(0,p_from_column); //wrap around for missing begining. - if (p_search_flags&SEARCH_BACKWARDS) - from_column=text_line.length()-1; - else - from_column=0; - } - - pos=-1; - - if (!(p_search_flags&SEARCH_BACKWARDS)) { - - pos = (p_search_flags&SEARCH_MATCH_CASE)?text_line.find(p_key,from_column):text_line.findn(p_key,from_column); - } else { - - pos = (p_search_flags&SEARCH_MATCH_CASE)?text_line.rfind(p_key,from_column):text_line.rfindn(p_key,from_column); - } - - if (pos!=-1 && (p_search_flags&SEARCH_WHOLE_WORDS)) { - //validate for whole words - if (pos>0 && _is_text_char(text_line[pos-1])) - pos=-1; - else if (_is_text_char(text_line[pos+p_key.length()])) - pos=-1; - } - - if (pos!=-1) - break; - - if (p_search_flags&SEARCH_BACKWARDS) - line--; - else - line++; - - } - - if (pos==-1) { - r_line=-1; - r_column=-1; - return false; - } - - r_line=line; - r_column=pos; - - - return true; + + if (p_key.length()==0) + return false; + ERR_FAIL_INDEX_V(p_from_line,text.size(),false); + ERR_FAIL_INDEX_V(p_from_column,text[p_from_line].length()+1,false); + + //search through the whole documment, but start by current line + + int line=-1; + int pos=-1; + + line=p_from_line; + + for(int i=0;i<text.size()+1;i++) { + //backwards is broken... + //int idx=(p_search_flags&SEARCH_BACKWARDS)?(text.size()-i):i; //do backwards seearch + + + if (line<0) { + line=text.size()-1; + } + if (line==text.size()) { + line=0; + } + + String text_line = text[line]; + int from_column=0; + if (line==p_from_line) { + + if (i==text.size()) { + //wrapped + + if (p_search_flags&SEARCH_BACKWARDS) { + text_line=text_line.substr(from_column,text_line.length()); + from_column=text_line.length(); + } else { + text_line=text_line.substr(0,from_column); + from_column=0; + } + + } else { + + from_column=p_from_column; + } + + + } else { + //text_line=text_line.substr(0,p_from_column); //wrap around for missing begining. + if (p_search_flags&SEARCH_BACKWARDS) + from_column=text_line.length()-1; + else + from_column=0; + } + + pos=-1; + + if (!(p_search_flags&SEARCH_BACKWARDS)) { + + pos = (p_search_flags&SEARCH_MATCH_CASE)?text_line.find(p_key,from_column):text_line.findn(p_key,from_column); + } else { + + pos = (p_search_flags&SEARCH_MATCH_CASE)?text_line.rfind(p_key,from_column):text_line.rfindn(p_key,from_column); + } + + if (pos!=-1 && (p_search_flags&SEARCH_WHOLE_WORDS)) { + //validate for whole words + if (pos>0 && _is_text_char(text_line[pos-1])) + pos=-1; + else if (_is_text_char(text_line[pos+p_key.length()])) + pos=-1; + } + + if (pos!=-1) + break; + + if (p_search_flags&SEARCH_BACKWARDS) + line--; + else + line++; + + } + + if (pos==-1) { + r_line=-1; + r_column=-1; + return false; + } + + r_line=line; + r_column=pos; + + + return true; } void TextEdit::_cursor_changed_emit() { - - emit_signal("cursor_changed"); - cursor_changed_dirty=false; + + emit_signal("cursor_changed"); + cursor_changed_dirty=false; } void TextEdit::_text_changed_emit() { - - emit_signal("text_changed"); - text_changed_dirty=false; + + emit_signal("text_changed"); + text_changed_dirty=false; } void TextEdit::set_line_as_marked(int p_line,bool p_marked) { - - ERR_FAIL_INDEX(p_line,text.size()); - text.set_marked(p_line,p_marked); - update(); + + ERR_FAIL_INDEX(p_line,text.size()); + text.set_marked(p_line,p_marked); + update(); } bool TextEdit::is_line_set_as_breakpoint(int p_line) const { - - ERR_FAIL_INDEX_V(p_line,text.size(),false); - return text.is_breakpoint(p_line); - + + ERR_FAIL_INDEX_V(p_line,text.size(),false); + return text.is_breakpoint(p_line); + } void TextEdit::set_line_as_breakpoint(int p_line,bool p_breakpoint) { - - - ERR_FAIL_INDEX(p_line,text.size()); - text.set_breakpoint(p_line,p_breakpoint); - update(); + + + ERR_FAIL_INDEX(p_line,text.size()); + text.set_breakpoint(p_line,p_breakpoint); + update(); } void TextEdit::get_breakpoints(List<int> *p_breakpoints) const { - - for(int i=0;i<text.size();i++) { - if (text.is_breakpoint(i)) - p_breakpoints->push_back(i); - } + + for(int i=0;i<text.size();i++) { + if (text.is_breakpoint(i)) + p_breakpoints->push_back(i); + } } int TextEdit::get_line_count() const { - - return text.size(); + + return text.size(); } void TextEdit::_do_text_op(const TextOperation& p_op, bool p_reverse) { - - ERR_FAIL_COND(p_op.type==TextOperation::TYPE_NONE); - - bool insert = p_op.type==TextOperation::TYPE_INSERT; - if (p_reverse) - insert=!insert; - - if (insert) { - - int check_line; - int check_column; - _base_insert_text(p_op.from_line,p_op.from_column,p_op.text,check_line,check_column); - ERR_FAIL_COND( check_line != p_op.to_line ); // BUG - ERR_FAIL_COND( check_column != p_op.to_column ); // BUG - } else { - - _base_remove_text(p_op.from_line,p_op.from_column,p_op.to_line,p_op.to_column); - } - + + ERR_FAIL_COND(p_op.type==TextOperation::TYPE_NONE); + + bool insert = p_op.type==TextOperation::TYPE_INSERT; + if (p_reverse) + insert=!insert; + + if (insert) { + + int check_line; + int check_column; + _base_insert_text(p_op.from_line,p_op.from_column,p_op.text,check_line,check_column); + ERR_FAIL_COND( check_line != p_op.to_line ); // BUG + ERR_FAIL_COND( check_column != p_op.to_column ); // BUG + } else { + + _base_remove_text(p_op.from_line,p_op.from_column,p_op.to_line,p_op.to_column); + } + } void TextEdit::_clear_redo() { - - if (undo_stack_pos==NULL) - return; //nothing to clear - - _push_current_op(); - - while (undo_stack_pos) { - List<TextOperation>::Element *elem = undo_stack_pos; - undo_stack_pos=undo_stack_pos->next(); - undo_stack.erase(elem); - } + + if (undo_stack_pos==NULL) + return; //nothing to clear + + _push_current_op(); + + while (undo_stack_pos) { + List<TextOperation>::Element *elem = undo_stack_pos; + undo_stack_pos=undo_stack_pos->next(); + undo_stack.erase(elem); + } } void TextEdit::undo() { - - _push_current_op(); - - if (undo_stack_pos==NULL) { - - if (!undo_stack.size()) - return; //nothing to undo - - undo_stack_pos=undo_stack.back(); - - } else if (undo_stack_pos==undo_stack.front()) - return; // at the bottom of the undo stack - else - undo_stack_pos=undo_stack_pos->prev(); - - _do_text_op( undo_stack_pos->get(),true); - if(undo_stack_pos->get().chain_backward) { - do { - undo_stack_pos = undo_stack_pos->prev(); - _do_text_op(undo_stack_pos->get(), true); - } while(!undo_stack_pos->get().chain_forward); - } - - cursor_set_line(undo_stack_pos->get().from_line); - cursor_set_column(undo_stack_pos->get().from_column); - update(); + + _push_current_op(); + + if (undo_stack_pos==NULL) { + + if (!undo_stack.size()) + return; //nothing to undo + + undo_stack_pos=undo_stack.back(); + + } else if (undo_stack_pos==undo_stack.front()) + return; // at the bottom of the undo stack + else + undo_stack_pos=undo_stack_pos->prev(); + + _do_text_op( undo_stack_pos->get(),true); + if(undo_stack_pos->get().chain_backward) { + do { + undo_stack_pos = undo_stack_pos->prev(); + _do_text_op(undo_stack_pos->get(), true); + } while(!undo_stack_pos->get().chain_forward); + } + + cursor_set_line(undo_stack_pos->get().from_line); + cursor_set_column(undo_stack_pos->get().from_column); + update(); } void TextEdit::redo() { - - _push_current_op(); - - if (undo_stack_pos==NULL) - return; //nothing to do. - - _do_text_op(undo_stack_pos->get(), false); - if(undo_stack_pos->get().chain_forward) { - do { - undo_stack_pos=undo_stack_pos->next(); - _do_text_op(undo_stack_pos->get(), false); - } while(!undo_stack_pos->get().chain_backward); - } - cursor_set_line(undo_stack_pos->get().from_line); - cursor_set_column(undo_stack_pos->get().from_column); - undo_stack_pos=undo_stack_pos->next(); - update(); + + _push_current_op(); + + if (undo_stack_pos==NULL) + return; //nothing to do. + + _do_text_op(undo_stack_pos->get(), false); + if(undo_stack_pos->get().chain_forward) { + do { + undo_stack_pos=undo_stack_pos->next(); + _do_text_op(undo_stack_pos->get(), false); + } while(!undo_stack_pos->get().chain_backward); + } + cursor_set_line(undo_stack_pos->get().from_line); + cursor_set_column(undo_stack_pos->get().from_column); + undo_stack_pos=undo_stack_pos->next(); + update(); } void TextEdit::clear_undo_history() { - - saved_version=0; - current_op.type=TextOperation::TYPE_NONE; - undo_stack_pos=NULL; - undo_stack.clear(); - + + saved_version=0; + current_op.type=TextOperation::TYPE_NONE; + undo_stack_pos=NULL; + undo_stack.clear(); + } void TextEdit::_begin_compex_operation() { - _push_current_op(); - next_operation_is_complex=true; + _push_current_op(); + next_operation_is_complex=true; } void TextEdit::_end_compex_operation() { - - _push_current_op(); - ERR_FAIL_COND(undo_stack.size() == 0); - - if(undo_stack.back()->get().chain_forward) { - undo_stack.back()->get().chain_forward=false; - return; - } - - undo_stack.back()->get().chain_backward=true; + + _push_current_op(); + ERR_FAIL_COND(undo_stack.size() == 0); + + if(undo_stack.back()->get().chain_forward) { + undo_stack.back()->get().chain_forward=false; + return; + } + + undo_stack.back()->get().chain_backward=true; } void TextEdit::_push_current_op() { - - if (current_op.type==TextOperation::TYPE_NONE) - return; // do nothing - - if(next_operation_is_complex) { - current_op.chain_forward=true; - next_operation_is_complex=false; - } - - undo_stack.push_back(current_op); - current_op.type=TextOperation::TYPE_NONE; - current_op.text=""; - current_op.chain_forward=false; - + + if (current_op.type==TextOperation::TYPE_NONE) + return; // do nothing + + if(next_operation_is_complex) { + current_op.chain_forward=true; + next_operation_is_complex=false; + } + + undo_stack.push_back(current_op); + current_op.type=TextOperation::TYPE_NONE; + current_op.text=""; + current_op.chain_forward=false; + } void TextEdit::set_draw_tabs(bool p_draw) { - - draw_tabs=p_draw; + + draw_tabs=p_draw; } bool TextEdit::is_drawing_tabs() const{ - - return draw_tabs; + + return draw_tabs; } uint32_t TextEdit::get_version() const { - return current_op.version; + return current_op.version; } uint32_t TextEdit::get_saved_version() const { - - return saved_version; + + return saved_version; } void TextEdit::tag_saved_version() { - - saved_version=get_version(); + + saved_version=get_version(); } int TextEdit::get_v_scroll() const { - - return v_scroll->get_val(); + + return v_scroll->get_val(); } void TextEdit::set_v_scroll(int p_scroll) { - - v_scroll->set_val(p_scroll); - cursor.line_ofs=p_scroll; + + v_scroll->set_val(p_scroll); + cursor.line_ofs=p_scroll; } int TextEdit::get_h_scroll() const { - - return h_scroll->get_val(); + + return h_scroll->get_val(); } void TextEdit::set_h_scroll(int p_scroll) { - - h_scroll->set_val(p_scroll); + + h_scroll->set_val(p_scroll); } void TextEdit::set_completion(bool p_enabled,const Vector<String>& p_prefixes) { - - completion_prefixes.clear(); - completion_enabled=p_enabled; - for(int i=0;i<p_prefixes.size();i++) - completion_prefixes.insert(p_prefixes[i]); + + completion_prefixes.clear(); + completion_enabled=p_enabled; + for(int i=0;i<p_prefixes.size();i++) + completion_prefixes.insert(p_prefixes[i]); } void TextEdit::_confirm_completion() { - - String remaining=completion_current.substr(completion_base.length(),completion_current.length()-completion_base.length()); - String l = text[cursor.line]; - bool same=true; - //if what is going to be inserted is the same as what it is, don't change it - for(int i=0;i<remaining.length();i++) { - int c=i+cursor.column; - if (c>=l.length() || l[c]!=remaining[i]) { - same=false; - break; - } - } - - if (same) - cursor_set_column(cursor.column+remaining.length()); - else { - insert_text_at_cursor(remaining); - if (remaining.ends_with("(") && auto_brace_completion_enabled) { - insert_text_at_cursor(")"); - cursor.column--; + + String remaining=completion_current.substr(completion_base.length(),completion_current.length()-completion_base.length()); + String l = text[cursor.line]; + bool same=true; + //if what is going to be inserted is the same as what it is, don't change it + for(int i=0;i<remaining.length();i++) { + int c=i+cursor.column; + if (c>=l.length() || l[c]!=remaining[i]) { + same=false; + break; + } } - } - - _cancel_completion(); + + if (same) + cursor_set_column(cursor.column+remaining.length()); + else { + insert_text_at_cursor(remaining); + if (remaining.ends_with("(") && auto_brace_completion_enabled) { + insert_text_at_cursor(")"); + cursor.column--; + } + } + + _cancel_completion(); } @@ -3196,185 +3425,240 @@ void TextEdit::_cancel_code_hint() { } void TextEdit::_cancel_completion() { - - if (!completion_active) - return; - - completion_active=false; - update(); - + + if (!completion_active) + return; + + completion_active=false; + update(); + } static bool _is_completable(CharType c) { - + return !_is_symbol(c) || c=='"' || c=='\''; } void TextEdit::_update_completion_candidates() { + + String l = text[cursor.line]; + int cofs = CLAMP(cursor.column,0,l.length()); + + + String s; + + //look for keywords first + + bool inquote=false; + int first_quote=-1; + + int c=cofs-1; + while(c>=0) { + if (l[c]=='"' || l[c]=='\'') { + inquote=!inquote; + if (first_quote==-1) + first_quote=c; + } + c--; + } - String l = text[cursor.line]; - int cofs = CLAMP(cursor.column,0,l.length()); - - - String s; - - while(cofs>0 && l[cofs-1]>32 && _is_completable(l[cofs-1])) { - s=String::chr(l[cofs-1])+s; - if (l[cofs-1]=='\'' || l[cofs-1]=='"') - break; - - cofs--; - } - - - update(); - - if (s=="" && (cofs==0 || !completion_prefixes.has(String::chr(l[cofs-1])))) { - //none to complete, cancel - _cancel_completion(); - return; - } + bool pre_keyword=false; + bool cancel=false; - completion_options.clear(); - completion_index=0; - completion_base=s; - int ci_match=0; - for(int i=0;i<completion_strings.size();i++) { - if (completion_strings[i].begins_with(s)) { - completion_options.push_back(completion_strings[i]); - int m=0; - int max=MIN(completion_current.length(),completion_strings[i].length()); - if (max<ci_match) - continue; - for(int j=0;j<max;j++) { + //print_line("inquote: "+itos(inquote)+"first quote "+itos(first_quote)+" cofs-1 "+itos(cofs-1)); + if (!inquote && first_quote==cofs-1) { + //no completion here + //print_line("cancel!"); + cancel=true; + } if (inquote && first_quote!=-1) { - if (j>=completion_strings[i].length()) - break; - if (completion_current[j]!=completion_strings[i][j]) - break; - m++; - } - if (m>ci_match) { - ci_match=m; - completion_index=completion_options.size()-1; - } + s=l.substr(first_quote,cofs-first_quote); + //print_line("s: 1"+s); + } else if (cofs>0 && l[cofs-1]==' ') { + int kofs=cofs-1; + String kw; + while (kofs>=0 && l[kofs]==' ') + kofs--; - } - } + while(kofs>=0 && l[kofs]>32 && _is_completable(l[kofs])) { + kw=String::chr(l[kofs])+kw; + kofs--; + } + pre_keyword=keywords.has(kw); + //print_line("KW "+kw+"? "+itos(pre_keyword)); + } else { - if (completion_options.size()==0) { - //no options to complete, cancel - _cancel_completion(); - return; - } + while(cofs>0 && l[cofs-1]>32 && _is_completable(l[cofs-1])) { + s=String::chr(l[cofs-1])+s; + if (l[cofs-1]=='\'' || l[cofs-1]=='"') + break; - completion_current=completion_options[completion_index]; + cofs--; + } + } + + update(); + + if (cancel || (!pre_keyword && s=="" && (cofs==0 || !completion_prefixes.has(String::chr(l[cofs-1]))))) { + //none to complete, cancel + _cancel_completion(); + return; + } + + completion_options.clear(); + completion_index=0; + completion_base=s; + int ci_match=0; + for(int i=0;i<completion_strings.size();i++) { + if (completion_strings[i].begins_with(s)) { + completion_options.push_back(completion_strings[i]); + int m=0; + int max=MIN(completion_current.length(),completion_strings[i].length()); + if (max<ci_match) + continue; + for(int j=0;j<max;j++) { + + if (j>=completion_strings[i].length()) + break; + if (completion_current[j]!=completion_strings[i][j]) + break; + m++; + } + if (m>ci_match) { + ci_match=m; + completion_index=completion_options.size()-1; + } + + } + } + + + + if (completion_options.size()==0) { + //no options to complete, cancel + _cancel_completion(); + return; + + } + + completion_current=completion_options[completion_index]; + #if 0 // even there's only one option, user still get the chance to choose using it or not - if (completion_options.size()==1) { - //one option to complete, just complete it automagically - _confirm_completion(); -// insert_text_at_cursor(completion_options[0].substr(s.length(),completion_options[0].length()-s.length())); - _cancel_completion(); - return; - - } + if (completion_options.size()==1) { + //one option to complete, just complete it automagically + _confirm_completion(); + // insert_text_at_cursor(completion_options[0].substr(s.length(),completion_options[0].length()-s.length())); + _cancel_completion(); + return; + + } #endif - if (completion_options.size()==1 && s==completion_options[0]) - _cancel_completion(); - - completion_enabled=true; + if (completion_options.size()==1 && s==completion_options[0]) + _cancel_completion(); + + completion_enabled=true; } void TextEdit::query_code_comple() { - + String l = text[cursor.line]; int ofs = CLAMP(cursor.column,0,l.length()); + + bool inquote=false; + + int c=ofs-1; + while(c>=0) { + if (l[c]=='"' || l[c]=='\'') + inquote=!inquote; + c--; + } - if (ofs>0 && (_is_completable(l[ofs-1]) || completion_prefixes.has(String::chr(l[ofs-1])))) + if (ofs>0 && (inquote || _is_completable(l[ofs-1]) || completion_prefixes.has(String::chr(l[ofs-1])))) emit_signal("request_completion"); - + } void TextEdit::set_code_hint(const String& p_hint) { - + completion_hint=p_hint; completion_hint_offset=-0xFFFF; update(); } void TextEdit::code_complete(const Vector<String> &p_strings) { - - - completion_strings=p_strings; - completion_active=true; - completion_current=""; - completion_index=0; - _update_completion_candidates(); -// + + + completion_strings=p_strings; + completion_active=true; + completion_current=""; + completion_index=0; + _update_completion_candidates(); + // } String TextEdit::get_tooltip(const Point2& p_pos) const { - - if (!tooltip_obj) - return Control::get_tooltip(p_pos); - int row,col; - if (!_get_mouse_pos(p_pos, row,col)) { - return Control::get_tooltip(p_pos); - } - - String s = text[row]; - if (s.length()==0) - return Control::get_tooltip(p_pos); - int beg=CLAMP(col,0,s.length()); - int end=beg; - - - if (s[beg]>32 || beg==s.length()) { - - bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this - - while(beg>0 && s[beg-1]>32 && (symbol==_is_symbol(s[beg-1]))) { - beg--; - } - while(end<s.length() && s[end+1]>32 && (symbol==_is_symbol(s[end+1]))) { - end++; - } - - if (end<s.length()) - end+=1; - - String tt = tooltip_obj->call(tooltip_func,s.substr(beg,end-beg),tooltip_ud); - - return tt; - - } - - return Control::get_tooltip(p_pos); - + + if (!tooltip_obj) + return Control::get_tooltip(p_pos); + int row,col; + _get_mouse_pos(p_pos, row, col); + + String s = text[row]; + if (s.length()==0) + return Control::get_tooltip(p_pos); + int beg=CLAMP(col,0,s.length()); + int end=beg; + + + if (s[beg]>32 || beg==s.length()) { + + bool symbol = beg < s.length() && _is_symbol(s[beg]); //not sure if right but most editors behave like this + + while(beg>0 && s[beg-1]>32 && (symbol==_is_symbol(s[beg-1]))) { + beg--; + } + while(end<s.length() && s[end+1]>32 && (symbol==_is_symbol(s[end+1]))) { + end++; + } + + if (end<s.length()) + end+=1; + + String tt = tooltip_obj->call(tooltip_func,s.substr(beg,end-beg),tooltip_ud); + + return tt; + + } + + return Control::get_tooltip(p_pos); + } void TextEdit::set_tooltip_request_func(Object *p_obj, const StringName& p_function,const Variant& p_udata) { - - tooltip_obj=p_obj; - tooltip_func=p_function; - tooltip_ud=p_udata; + + tooltip_obj=p_obj; + tooltip_func=p_function; + tooltip_ud=p_udata; } void TextEdit::set_line(int line, String new_text) { - if (line < 0 || line > text.size()) - return; - _remove_text(line, 0, line, text[line].length()); - _insert_text(line, 0, new_text); + if (line < 0 || line > text.size()) + return; + _remove_text(line, 0, line, text[line].length()); + _insert_text(line, 0, new_text); + if (cursor.line==line) { + cursor.column=MIN(cursor.column,new_text.length()); + } } void TextEdit::insert_at(const String &p_text, int at) @@ -3385,167 +3669,174 @@ void TextEdit::insert_at(const String &p_text, int at) } void TextEdit::set_show_line_numbers(bool p_show) { - - line_numbers=p_show; - update(); + + line_numbers=p_show; + update(); } +bool TextEdit::is_text_field() const { + return true; +} void TextEdit::_bind_methods() { - - - ObjectTypeDB::bind_method(_MD("_input_event"),&TextEdit::_input_event); - ObjectTypeDB::bind_method(_MD("_scroll_moved"),&TextEdit::_scroll_moved); - ObjectTypeDB::bind_method(_MD("_cursor_changed_emit"),&TextEdit::_cursor_changed_emit); - ObjectTypeDB::bind_method(_MD("_text_changed_emit"),&TextEdit::_text_changed_emit); - ObjectTypeDB::bind_method(_MD("_push_current_op"),&TextEdit::_push_current_op); - - BIND_CONSTANT( SEARCH_MATCH_CASE ); - BIND_CONSTANT( SEARCH_WHOLE_WORDS ); - BIND_CONSTANT( SEARCH_BACKWARDS ); - -/* + + + ObjectTypeDB::bind_method(_MD("_input_event"),&TextEdit::_input_event); + ObjectTypeDB::bind_method(_MD("_scroll_moved"),&TextEdit::_scroll_moved); + ObjectTypeDB::bind_method(_MD("_cursor_changed_emit"),&TextEdit::_cursor_changed_emit); + ObjectTypeDB::bind_method(_MD("_text_changed_emit"),&TextEdit::_text_changed_emit); + ObjectTypeDB::bind_method(_MD("_push_current_op"),&TextEdit::_push_current_op); + + BIND_CONSTANT( SEARCH_MATCH_CASE ); + BIND_CONSTANT( SEARCH_WHOLE_WORDS ); + BIND_CONSTANT( SEARCH_BACKWARDS ); + + /* ObjectTypeDB::bind_method(_MD("delete_char"),&TextEdit::delete_char); ObjectTypeDB::bind_method(_MD("delete_line"),&TextEdit::delete_line); */ - - ObjectTypeDB::bind_method(_MD("set_text","text"),&TextEdit::set_text); - ObjectTypeDB::bind_method(_MD("insert_text_at_cursor","text"),&TextEdit::insert_text_at_cursor); - - ObjectTypeDB::bind_method(_MD("get_line_count"),&TextEdit::get_line_count); - ObjectTypeDB::bind_method(_MD("get_text"),&TextEdit::get_text); - ObjectTypeDB::bind_method(_MD("get_line"),&TextEdit::get_line); - - ObjectTypeDB::bind_method(_MD("cursor_set_column","column"),&TextEdit::cursor_set_column); - ObjectTypeDB::bind_method(_MD("cursor_set_line","line"),&TextEdit::cursor_set_line); - - ObjectTypeDB::bind_method(_MD("cursor_get_column"),&TextEdit::cursor_get_column); - ObjectTypeDB::bind_method(_MD("cursor_get_line"),&TextEdit::cursor_get_line); - - - ObjectTypeDB::bind_method(_MD("set_readonly","enable"),&TextEdit::set_readonly); - ObjectTypeDB::bind_method(_MD("set_wrap","enable"),&TextEdit::set_wrap); - ObjectTypeDB::bind_method(_MD("set_max_chars","amount"),&TextEdit::set_max_chars); - - ObjectTypeDB::bind_method(_MD("cut"),&TextEdit::cut); - ObjectTypeDB::bind_method(_MD("copy"),&TextEdit::copy); - ObjectTypeDB::bind_method(_MD("paste"),&TextEdit::paste); - ObjectTypeDB::bind_method(_MD("select_all"),&TextEdit::select_all); - ObjectTypeDB::bind_method(_MD("select","from_line","from_column","to_line","to_column"),&TextEdit::select); - - ObjectTypeDB::bind_method(_MD("is_selection_active"),&TextEdit::is_selection_active); - ObjectTypeDB::bind_method(_MD("get_selection_from_line"),&TextEdit::get_selection_from_line); - ObjectTypeDB::bind_method(_MD("get_selection_from_column"),&TextEdit::get_selection_from_column); - ObjectTypeDB::bind_method(_MD("get_selection_to_line"),&TextEdit::get_selection_to_line); - ObjectTypeDB::bind_method(_MD("get_selection_to_column"),&TextEdit::get_selection_to_column); - ObjectTypeDB::bind_method(_MD("get_selection_text"),&TextEdit::get_selection_text); - ObjectTypeDB::bind_method(_MD("get_word_under_cursor"),&TextEdit::get_word_under_cursor); - ObjectTypeDB::bind_method(_MD("search","flags","from_line","from_column","to_line","to_column"),&TextEdit::_search_bind); - - ObjectTypeDB::bind_method(_MD("undo"),&TextEdit::undo); - ObjectTypeDB::bind_method(_MD("redo"),&TextEdit::redo); - ObjectTypeDB::bind_method(_MD("clear_undo_history"),&TextEdit::clear_undo_history); - - ObjectTypeDB::bind_method(_MD("set_syntax_coloring","enable"),&TextEdit::set_syntax_coloring); - ObjectTypeDB::bind_method(_MD("is_syntax_coloring_enabled"),&TextEdit::is_syntax_coloring_enabled); - - - ObjectTypeDB::bind_method(_MD("add_keyword_color","keyword","color"),&TextEdit::add_keyword_color); - ObjectTypeDB::bind_method(_MD("add_color_region","begin_key","end_key","color","line_only"),&TextEdit::add_color_region,DEFVAL(false)); - ObjectTypeDB::bind_method(_MD("set_symbol_color","color"),&TextEdit::set_symbol_color); - ObjectTypeDB::bind_method(_MD("set_custom_bg_color","color"),&TextEdit::set_custom_bg_color); - ObjectTypeDB::bind_method(_MD("clear_colors"),&TextEdit::clear_colors); - - - ADD_SIGNAL(MethodInfo("cursor_changed")); - ADD_SIGNAL(MethodInfo("text_changed")); - ADD_SIGNAL(MethodInfo("request_completion")); - + + ObjectTypeDB::bind_method(_MD("set_text","text"),&TextEdit::set_text); + ObjectTypeDB::bind_method(_MD("insert_text_at_cursor","text"),&TextEdit::insert_text_at_cursor); + + ObjectTypeDB::bind_method(_MD("get_line_count"),&TextEdit::get_line_count); + ObjectTypeDB::bind_method(_MD("get_text"),&TextEdit::get_text); + ObjectTypeDB::bind_method(_MD("get_line"),&TextEdit::get_line); + + ObjectTypeDB::bind_method(_MD("cursor_set_column","column"),&TextEdit::cursor_set_column); + ObjectTypeDB::bind_method(_MD("cursor_set_line","line"),&TextEdit::cursor_set_line); + + ObjectTypeDB::bind_method(_MD("cursor_get_column"),&TextEdit::cursor_get_column); + ObjectTypeDB::bind_method(_MD("cursor_get_line"),&TextEdit::cursor_get_line); + + + ObjectTypeDB::bind_method(_MD("set_readonly","enable"),&TextEdit::set_readonly); + ObjectTypeDB::bind_method(_MD("set_wrap","enable"),&TextEdit::set_wrap); + ObjectTypeDB::bind_method(_MD("set_max_chars","amount"),&TextEdit::set_max_chars); + + ObjectTypeDB::bind_method(_MD("cut"),&TextEdit::cut); + ObjectTypeDB::bind_method(_MD("copy"),&TextEdit::copy); + ObjectTypeDB::bind_method(_MD("paste"),&TextEdit::paste); + ObjectTypeDB::bind_method(_MD("select_all"),&TextEdit::select_all); + ObjectTypeDB::bind_method(_MD("select","from_line","from_column","to_line","to_column"),&TextEdit::select); + + ObjectTypeDB::bind_method(_MD("is_selection_active"),&TextEdit::is_selection_active); + ObjectTypeDB::bind_method(_MD("get_selection_from_line"),&TextEdit::get_selection_from_line); + ObjectTypeDB::bind_method(_MD("get_selection_from_column"),&TextEdit::get_selection_from_column); + ObjectTypeDB::bind_method(_MD("get_selection_to_line"),&TextEdit::get_selection_to_line); + ObjectTypeDB::bind_method(_MD("get_selection_to_column"),&TextEdit::get_selection_to_column); + ObjectTypeDB::bind_method(_MD("get_selection_text"),&TextEdit::get_selection_text); + ObjectTypeDB::bind_method(_MD("get_word_under_cursor"),&TextEdit::get_word_under_cursor); + ObjectTypeDB::bind_method(_MD("search","flags","from_line","from_column","to_line","to_column"),&TextEdit::_search_bind); + + ObjectTypeDB::bind_method(_MD("undo"),&TextEdit::undo); + ObjectTypeDB::bind_method(_MD("redo"),&TextEdit::redo); + ObjectTypeDB::bind_method(_MD("clear_undo_history"),&TextEdit::clear_undo_history); + + ObjectTypeDB::bind_method(_MD("set_syntax_coloring","enable"),&TextEdit::set_syntax_coloring); + ObjectTypeDB::bind_method(_MD("is_syntax_coloring_enabled"),&TextEdit::is_syntax_coloring_enabled); + + + ObjectTypeDB::bind_method(_MD("add_keyword_color","keyword","color"),&TextEdit::add_keyword_color); + ObjectTypeDB::bind_method(_MD("add_color_region","begin_key","end_key","color","line_only"),&TextEdit::add_color_region,DEFVAL(false)); + ObjectTypeDB::bind_method(_MD("set_symbol_color","color"),&TextEdit::set_symbol_color); + ObjectTypeDB::bind_method(_MD("set_custom_bg_color","color"),&TextEdit::set_custom_bg_color); + ObjectTypeDB::bind_method(_MD("clear_colors"),&TextEdit::clear_colors); + + + ADD_SIGNAL(MethodInfo("cursor_changed")); + ADD_SIGNAL(MethodInfo("text_changed")); + ADD_SIGNAL(MethodInfo("request_completion")); + } TextEdit::TextEdit() { - - readonly=false; - setting_row=false; - draw_tabs=false; - max_chars=0; - clear(); - wrap=false; - set_focus_mode(FOCUS_ALL); - _update_caches(); - cache.size=Size2(1,1); - tab_size=4; - text.set_tab_size(tab_size); - text.clear(); -// text.insert(1,"Mongolia.."); -// text.insert(2,"PAIS GENEROSO!!"); - text.set_color_regions(&color_regions); - - h_scroll = memnew( HScrollBar ); - v_scroll = memnew( VScrollBar ); - - add_child(h_scroll); - add_child(v_scroll); - - updating_scrolls=false; - selection.active=false; - - h_scroll->connect("value_changed", this,"_scroll_moved"); - v_scroll->connect("value_changed", this,"_scroll_moved"); - - cursor_changed_dirty=false; - text_changed_dirty=false; - - selection.selecting_mode=Selection::MODE_NONE; - selection.selecting_line=0; - selection.selecting_column=0; - selection.selecting_test=false; - selection.active=false; - syntax_coloring=false; - - custom_bg_color=Color(0,0,0,0); - idle_detect = memnew( Timer ); - add_child(idle_detect); - idle_detect->set_one_shot(true); - idle_detect->set_wait_time(GLOBAL_DEF("display/text_edit_idle_detect_sec",3)); - idle_detect->connect("timeout", this,"_push_current_op"); - + + readonly=false; + setting_row=false; + draw_tabs=false; + max_chars=0; + clear(); + wrap=false; + set_focus_mode(FOCUS_ALL); + _update_caches(); + cache.size=Size2(1,1); + cache.row_height=1; + cache.line_spacing=1; + cache.line_number_w=1; + + tab_size=4; + text.set_tab_size(tab_size); + text.clear(); + // text.insert(1,"Mongolia.."); + // text.insert(2,"PAIS GENEROSO!!"); + text.set_color_regions(&color_regions); + + h_scroll = memnew( HScrollBar ); + v_scroll = memnew( VScrollBar ); + + add_child(h_scroll); + add_child(v_scroll); + + updating_scrolls=false; + selection.active=false; + + h_scroll->connect("value_changed", this,"_scroll_moved"); + v_scroll->connect("value_changed", this,"_scroll_moved"); + + cursor_changed_dirty=false; + text_changed_dirty=false; + + selection.selecting_mode=Selection::MODE_NONE; + selection.selecting_line=0; + selection.selecting_column=0; + selection.selecting_text=false; + selection.active=false; + syntax_coloring=false; + + custom_bg_color=Color(0,0,0,0); + idle_detect = memnew( Timer ); + add_child(idle_detect); + idle_detect->set_one_shot(true); + idle_detect->set_wait_time(GLOBAL_DEF("display/text_edit_idle_detect_sec",3)); + idle_detect->connect("timeout", this,"_push_current_op"); + #if 0 - syntax_coloring=true; - keywords["void"]=Color(0.3,0.0,0.1); - keywords["int"]=Color(0.3,0.0,0.1); - keywords["function"]=Color(0.3,0.0,0.1); - keywords["class"]=Color(0.3,0.0,0.1); - keywords["extends"]=Color(0.3,0.0,0.1); - keywords["constructor"]=Color(0.3,0.0,0.1); - symbol_color=Color(0.1,0.0,0.3,1.0); - - color_regions.push_back(ColorRegion("/*","*/",Color(0.4,0.6,0,4))); - color_regions.push_back(ColorRegion("//","",Color(0.6,0.6,0.4))); - color_regions.push_back(ColorRegion("\"","\"",Color(0.4,0.7,0.7))); - color_regions.push_back(ColorRegion("'","'",Color(0.4,0.8,0.8))); - color_regions.push_back(ColorRegion("#","",Color(0.2,1.0,0.2))); - + syntax_coloring=true; + keywords["void"]=Color(0.3,0.0,0.1); + keywords["int"]=Color(0.3,0.0,0.1); + keywords["function"]=Color(0.3,0.0,0.1); + keywords["class"]=Color(0.3,0.0,0.1); + keywords["extends"]=Color(0.3,0.0,0.1); + keywords["constructor"]=Color(0.3,0.0,0.1); + symbol_color=Color(0.1,0.0,0.3,1.0); + + color_regions.push_back(ColorRegion("/*","*/",Color(0.4,0.6,0,4))); + color_regions.push_back(ColorRegion("//","",Color(0.6,0.6,0.4))); + color_regions.push_back(ColorRegion("\"","\"",Color(0.4,0.7,0.7))); + color_regions.push_back(ColorRegion("'","'",Color(0.4,0.8,0.8))); + color_regions.push_back(ColorRegion("#","",Color(0.2,1.0,0.2))); + #endif - - current_op.type=TextOperation::TYPE_NONE; - undo_enabled=true; - undo_stack_pos=NULL; - setting_text=false; - last_dblclk=0; - current_op.version=0; - version=0; - saved_version=0; - - completion_enabled=false; - completion_active=false; - completion_line_ofs=0; - tooltip_obj=NULL; - line_numbers=false; - next_operation_is_complex=false; - auto_brace_completion_enabled=false; - brace_matching_enabled=false; - + + current_op.type=TextOperation::TYPE_NONE; + undo_enabled=true; + undo_stack_pos=NULL; + setting_text=false; + last_dblclk=0; + current_op.version=0; + version=0; + saved_version=0; + + completion_enabled=false; + completion_active=false; + completion_line_ofs=0; + tooltip_obj=NULL; + line_numbers=false; + next_operation_is_complex=false; + auto_brace_completion_enabled=false; + brace_matching_enabled=false; + } TextEdit::~TextEdit() diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h index ed4d30a9d2..059e15dcff 100644 --- a/scene/gui/text_edit.h +++ b/scene/gui/text_edit.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -55,7 +55,7 @@ class TextEdit : public Control { Mode selecting_mode; int selecting_line,selecting_column; - bool selecting_test; + bool selecting_text; bool active; @@ -63,6 +63,7 @@ class TextEdit : public Control { int from_line,from_column; int to_line,to_column; + bool shiftclick_left; } selection; @@ -269,7 +270,7 @@ class TextEdit : public Control { void _confirm_completion(); void _update_completion_candidates(); - bool _get_mouse_pos(const Point2i& p_mouse, int &r_row, int &r_col) const; + void _get_mouse_pos(const Point2i& p_mouse, int &r_row, int &r_col) const; protected: @@ -323,8 +324,8 @@ public: update(); } - void cursor_set_column(int p_col); - void cursor_set_line(int p_row); + void cursor_set_column(int p_col, bool p_adjust_viewport=true); + void cursor_set_line(int p_row, bool p_adjust_viewport=true); int cursor_get_column() const; int cursor_get_line() const; @@ -392,6 +393,7 @@ public: String get_text_for_completion(); + virtual bool is_text_field() const; TextEdit(); ~TextEdit(); }; diff --git a/scene/gui/texture_button.cpp b/scene/gui/texture_button.cpp index 7954ac65df..5b2caecb5b 100644 --- a/scene/gui/texture_button.cpp +++ b/scene/gui/texture_button.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,28 +31,37 @@ Size2 TextureButton::get_minimum_size() const { + Size2 rscale; if (normal.is_null()) { if (pressed.is_null()) { if (hover.is_null()) if (click_mask.is_null()) - return Size2(); + rscale= Size2(); else - return click_mask->get_size(); + rscale= click_mask->get_size(); else - return hover->get_size(); + rscale= hover->get_size(); } else - return pressed->get_size(); + rscale= pressed->get_size()*scale; } else - return normal->get_size(); + rscale= normal->get_size(); + + return rscale*scale; } bool TextureButton::has_point(const Point2& p_point) const { + if (scale[0] <= 0 || scale[1] <= 0) { + return false; + } + + Point2 ppos = p_point/scale; + if (click_mask.is_valid()) { - Point2i p =p_point; + Point2i p =ppos; if (p.x<0 || p.x>=click_mask->get_size().width || p.y<0 || p.y>=click_mask->get_size().height) return false; @@ -71,46 +80,57 @@ void TextureButton::_notification(int p_what) { DrawMode draw_mode = get_draw_mode(); // if (normal.is_null()) // break; + + Ref<Texture> texdraw; + switch (draw_mode) { case DRAW_NORMAL: { if (normal.is_valid()) - normal->draw(canvas_item,Point2()); + texdraw=normal; } break; case DRAW_PRESSED: { if (pressed.is_null()) { if (hover.is_null()) { if (normal.is_valid()) - normal->draw(canvas_item,Point2()); + texdraw=normal; } else - hover->draw(canvas_item,Point2()); + texdraw=hover; } else - pressed->draw(canvas_item,Point2()); + texdraw=pressed; } break; case DRAW_HOVER: { if (hover.is_null()) { if (pressed.is_valid() && is_pressed()) - pressed->draw(canvas_item,Point2()); + texdraw=pressed; else if (normal.is_valid()) - normal->draw(canvas_item,Point2()); + texdraw=normal; } else - hover->draw(canvas_item,Point2()); + texdraw=hover; } break; case DRAW_DISABLED: { if (disabled.is_null()) { if (normal.is_valid()) - normal->draw(canvas_item,Point2()); + texdraw=normal; } else - disabled->draw(canvas_item,Point2()); + texdraw=disabled; } break; } + + if (texdraw.is_valid()) { + Rect2 drect(Point2(),texdraw->get_size()*scale); + draw_texture_rect(texdraw,drect,false,modulate); + + } if (has_focus() && focused.is_valid()) { - focused->draw(canvas_item, Point2()); + Rect2 drect(Point2(),focused->get_size()*scale); + draw_texture_rect(focused,drect,false,modulate); + }; } break; @@ -125,6 +145,8 @@ void TextureButton::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_disabled_texture","texture:Texture"),&TextureButton::set_disabled_texture); ObjectTypeDB::bind_method(_MD("set_focused_texture","texture:Texture"),&TextureButton::set_focused_texture); ObjectTypeDB::bind_method(_MD("set_click_mask","mask:BitMap"),&TextureButton::set_click_mask); + ObjectTypeDB::bind_method(_MD("set_scale","scale"),&TextureButton::set_scale); + ObjectTypeDB::bind_method(_MD("set_modulate","color"),&TextureButton::set_modulate); ObjectTypeDB::bind_method(_MD("get_normal_texture:Texture"),&TextureButton::get_normal_texture); ObjectTypeDB::bind_method(_MD("get_pressed_texture:Texture"),&TextureButton::get_pressed_texture); @@ -132,13 +154,17 @@ void TextureButton::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_disabled_texture:Texture"),&TextureButton::get_disabled_texture); ObjectTypeDB::bind_method(_MD("get_focused_texture:Texture"),&TextureButton::get_focused_texture); ObjectTypeDB::bind_method(_MD("get_click_mask:BitMap"),&TextureButton::get_click_mask); - - ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"textures/normal",PROPERTY_HINT_RESOURCE_TYPE,"Texture"), _SCS("set_normal_texture"), _SCS("get_normal_texture")); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"textures/pressed",PROPERTY_HINT_RESOURCE_TYPE,"Texture"), _SCS("set_pressed_texture"), _SCS("get_pressed_texture")); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"textures/hover",PROPERTY_HINT_RESOURCE_TYPE,"Texture"), _SCS("set_hover_texture"), _SCS("get_hover_texture")); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"textures/disabled",PROPERTY_HINT_RESOURCE_TYPE,"Texture"), _SCS("set_disabled_texture"), _SCS("get_disabled_texture")); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"textures/focused",PROPERTY_HINT_RESOURCE_TYPE,"Texture"), _SCS("set_focused_texture"), _SCS("get_focused_texture")); - ADD_PROPERTY(PropertyInfo(Variant::OBJECT,"textures/click_mask",PROPERTY_HINT_RESOURCE_TYPE,"BitMap"), _SCS("set_click_mask"), _SCS("get_click_mask")) ; + ObjectTypeDB::bind_method(_MD("get_scale"),&TextureButton::get_scale); + ObjectTypeDB::bind_method(_MD("get_modulate"),&TextureButton::get_modulate); + + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT,"textures/normal",PROPERTY_HINT_RESOURCE_TYPE,"Texture"), _SCS("set_normal_texture"), _SCS("get_normal_texture")); + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT,"textures/pressed",PROPERTY_HINT_RESOURCE_TYPE,"Texture"), _SCS("set_pressed_texture"), _SCS("get_pressed_texture")); + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT,"textures/hover",PROPERTY_HINT_RESOURCE_TYPE,"Texture"), _SCS("set_hover_texture"), _SCS("get_hover_texture")); + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT,"textures/disabled",PROPERTY_HINT_RESOURCE_TYPE,"Texture"), _SCS("set_disabled_texture"), _SCS("get_disabled_texture")); + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT,"textures/focused",PROPERTY_HINT_RESOURCE_TYPE,"Texture"), _SCS("set_focused_texture"), _SCS("get_focused_texture")); + ADD_PROPERTYNZ(PropertyInfo(Variant::OBJECT,"textures/click_mask",PROPERTY_HINT_RESOURCE_TYPE,"BitMap"), _SCS("set_click_mask"), _SCS("get_click_mask")) ; + ADD_PROPERTYNO(PropertyInfo(Variant::VECTOR2,"params/scale",PROPERTY_HINT_RANGE,"0.01,1024,0.01"), _SCS("set_scale"), _SCS("get_scale")); + ADD_PROPERTYNO(PropertyInfo(Variant::COLOR,"params/modulate"), _SCS("set_modulate"), _SCS("get_modulate")); } @@ -206,6 +232,29 @@ void TextureButton::set_focused_texture(const Ref<Texture>& p_focused) { focused = p_focused; }; +void TextureButton::set_scale(Size2 p_scale) { + + scale=p_scale; + minimum_size_changed(); + update(); +} + +Size2 TextureButton::get_scale() const{ + + return scale; +} + +void TextureButton::set_modulate(const Color& p_modulate) { + modulate=p_modulate; + update(); +} + +Color TextureButton::get_modulate() const { + return modulate; +} + TextureButton::TextureButton() { + scale=Size2(1.0, 1.0); + modulate=Color(1,1,1); } diff --git a/scene/gui/texture_button.h b/scene/gui/texture_button.h index d186966cb1..01924c1c15 100644 --- a/scene/gui/texture_button.h +++ b/scene/gui/texture_button.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -41,6 +41,8 @@ class TextureButton : public BaseButton { Ref<Texture> disabled; Ref<Texture> focused; Ref<BitMap> click_mask; + Size2 scale; + Color modulate; protected: @@ -66,6 +68,11 @@ public: Ref<Texture> get_focused_texture() const; Ref<BitMap> get_click_mask() const; + void set_scale(Size2 p_scale); + Size2 get_scale() const; + + void set_modulate(const Color& p_modulate); + Color get_modulate() const; TextureButton(); }; diff --git a/scene/gui/texture_frame.cpp b/scene/gui/texture_frame.cpp index 26f4d32965..5a6bc86638 100644 --- a/scene/gui/texture_frame.cpp +++ b/scene/gui/texture_frame.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -77,9 +77,9 @@ void TextureFrame::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_expand","enable"), & TextureFrame::set_expand ); ObjectTypeDB::bind_method(_MD("has_expand"), & TextureFrame::has_expand ); - ADD_PROPERTY( PropertyInfo( Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), _SCS("set_texture"),_SCS("get_texture") ); - ADD_PROPERTY( PropertyInfo( Variant::COLOR, "modulate"), _SCS("set_modulate"),_SCS("get_modulate") ); - ADD_PROPERTY( PropertyInfo( Variant::BOOL, "expand" ), _SCS("set_expand"),_SCS("has_expand") ); + ADD_PROPERTYNZ( PropertyInfo( Variant::OBJECT, "texture", PROPERTY_HINT_RESOURCE_TYPE, "Texture"), _SCS("set_texture"),_SCS("get_texture") ); + ADD_PROPERTYNO( PropertyInfo( Variant::COLOR, "modulate"), _SCS("set_modulate"),_SCS("get_modulate") ); + ADD_PROPERTYNZ( PropertyInfo( Variant::BOOL, "expand" ), _SCS("set_expand"),_SCS("has_expand") ); } @@ -88,8 +88,8 @@ void TextureFrame::set_texture(const Ref<Texture>& p_tex) { texture=p_tex; update(); - if (texture.is_valid()) - texture->set_flags(texture->get_flags()&(~Texture::FLAG_REPEAT)); //remove repeat from texture, it looks bad in sprites + //if (texture.is_valid()) + // texture->set_flags(texture->get_flags()&(~Texture::FLAG_REPEAT)); //remove repeat from texture, it looks bad in sprites minimum_size_changed(); } diff --git a/scene/gui/texture_frame.h b/scene/gui/texture_frame.h index 9f75e1c2c0..0ccbf5a591 100644 --- a/scene/gui/texture_frame.h +++ b/scene/gui/texture_frame.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/texture_progress.cpp b/scene/gui/texture_progress.cpp index 0ce7df5d20..c8930add6e 100644 --- a/scene/gui/texture_progress.cpp +++ b/scene/gui/texture_progress.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -80,9 +80,50 @@ Ref<Texture> TextureProgress::get_progress_texture() const{ } +Point2 TextureProgress::unit_val_to_uv(float val) { + if (progress.is_null()) + return Point2(); + + if (val<0) + val+=1; + if (val>1) + val-=1; + + Point2 p=get_relative_center(); + + if (val<0.125) + return Point2(p.x+(1-p.x)*val*8,0); + if (val<0.25) + return Point2(1,p.y*(val-0.125)*8); + if (val<0.375) + return Point2(1,p.y+(1-p.y)*(val-0.25)*8); + if (val<0.5) + return Point2(1-(1-p.x)*(val-0.375)*8,1); + if (val<0.625) + return Point2(p.x*(1-(val-0.5)*8),1); + if (val<0.75) + return Point2(0,1-((1-p.y)*(val-0.625)*8)); + if (val<0.875) + return Point2(0,p.y-p.y*(val-0.75)*8); + else + return Point2(p.x*(val-0.875)*8,0); +} -void TextureProgress::_notification(int p_what){ +Point2 TextureProgress::get_relative_center() +{ + if (progress.is_null()) + return Point2(); + Point2 p = progress->get_size()/2; + p+=rad_center_off; + p.x/=progress->get_width(); + p.y/=progress->get_height(); + p.x=CLAMP(p.x,0,1); + p.y=CLAMP(p.y,0,1); + return p; +} +void TextureProgress::_notification(int p_what){ + const float corners[12]={-0.125,-0.375,-0.625,-0.875,0.125,0.375,0.625,0.875,1.125,1.375,1.625,1.875}; switch(p_what) { case NOTIFICATION_DRAW: { @@ -92,7 +133,69 @@ void TextureProgress::_notification(int p_what){ draw_texture(under,Point2()); if (progress.is_valid()) { Size2 s = progress->get_size(); - draw_texture_rect_region(progress,Rect2(Point2(),Size2(s.x*get_unit_value(),s.y)),Rect2(Point2(),Size2(s.x*get_unit_value(),s.y))); + switch (mode) { + case FILL_LEFT_TO_RIGHT: { + Rect2 region=Rect2(Point2(),Size2(s.x*get_unit_value(),s.y)); + draw_texture_rect_region(progress,region,region); + } break; + case FILL_RIGHT_TO_LEFT: { + Rect2 region=Rect2(Point2(s.x-s.x*get_unit_value(),0),Size2(s.x*get_unit_value(),s.y)); + draw_texture_rect_region(progress,region,region); + } break; + case FILL_TOP_TO_BOTTOM: { + Rect2 region=Rect2(Point2(),Size2(s.x,s.y*get_unit_value())); + draw_texture_rect_region(progress,region,region); + } break; + case FILL_BOTTOM_TO_TOP: { + Rect2 region=Rect2(Point2(0,s.y-s.y*get_unit_value()),Size2(s.x,s.y*get_unit_value())); + draw_texture_rect_region(progress,region,region); + } break; + case FILL_CLOCKWISE: + case FILL_COUNTER_CLOCKWISE: { + float val=get_unit_value()*rad_max_degrees/360; + if (val==1) { + Rect2 region=Rect2(Point2(),s); + draw_texture_rect_region(progress,region,region); + } else if (val!=0) { + Array pts; + float direction=mode==FILL_CLOCKWISE?1:-1; + float start=rad_init_angle/360; + float end=start+direction*val; + pts.append(start); + pts.append(end); + float from=MIN(start,end); + float to=MAX(start,end); + for (int i=0;i<12;i++) + if (corners[i]>from&&corners[i]<to) + pts.append(corners[i]); + pts.sort(); + Vector<Point2> uvs; + Vector<Point2> points; + uvs.push_back(get_relative_center()); + points.push_back(Point2(s.x*get_relative_center().x,s.y*get_relative_center().y)); + for (int i=0;i<pts.size();i++) { + Point2 uv=unit_val_to_uv(pts[i]); + if (uvs.find(uv)>=0) + continue; + uvs.push_back(uv); + points.push_back(Point2(uv.x*s.x,uv.y*s.y)); + } + draw_polygon(points,Vector<Color>(),uvs,progress); + } + if (get_tree()->is_editor_hint()) { + Point2 p=progress->get_size(); + p.x*=get_relative_center().x; + p.y*=get_relative_center().y; + p=p.floor(); + draw_line(p-Point2(8,0),p+Point2(8,0),Color(0.9,0.5,0.5),2); + draw_line(p-Point2(0,8),p+Point2(0,8),Color(0.9,0.5,0.5),2); + } + } break; + default: + draw_texture_rect_region(progress,Rect2(Point2(),Size2(s.x*get_unit_value(),s.y)),Rect2(Point2(),Size2(s.x*get_unit_value(),s.y))); + } + + } if (over.is_valid()) draw_texture(over,Point2()); @@ -101,6 +204,55 @@ void TextureProgress::_notification(int p_what){ } } +void TextureProgress::set_fill_mode(int p_fill) +{ + ERR_FAIL_INDEX(p_fill,6); + mode=(FillMode)p_fill; + update(); +} + +int TextureProgress::get_fill_mode() +{ + return mode; +} + +void TextureProgress::set_radial_initial_angle(float p_angle) +{ + while(p_angle>360) + p_angle-=360; + while (p_angle<0) + p_angle+=360; + rad_init_angle=p_angle; + update(); +} + +float TextureProgress::get_radial_initial_angle() +{ + return rad_init_angle; +} + +void TextureProgress::set_fill_degrees(float p_angle) +{ + rad_max_degrees=CLAMP(p_angle,0,360); + update(); +} + +float TextureProgress::get_fill_degrees() +{ + return rad_max_degrees; +} + +void TextureProgress::set_radial_center_offset(const Point2 &p_off) +{ + rad_center_off=p_off; + update(); +} + +Point2 TextureProgress::get_radial_center_offset() +{ + return rad_center_off; +} + void TextureProgress::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_under_texture","tex"),&TextureProgress::set_under_texture); @@ -112,13 +264,39 @@ void TextureProgress::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_over_texture","tex"),&TextureProgress::set_over_texture); ObjectTypeDB::bind_method(_MD("get_over_texture"),&TextureProgress::get_over_texture); + ObjectTypeDB::bind_method(_MD("set_fill_mode","mode"),&TextureProgress::set_fill_mode); + ObjectTypeDB::bind_method(_MD("get_fill_mode"), &TextureProgress::get_fill_mode); + + ObjectTypeDB::bind_method(_MD("set_radial_initial_angle","mode"),&TextureProgress::set_radial_initial_angle); + ObjectTypeDB::bind_method(_MD("get_radial_initial_angle"), &TextureProgress::get_radial_initial_angle); + + ObjectTypeDB::bind_method(_MD("set_radial_center_offset","mode"),&TextureProgress::set_radial_center_offset); + ObjectTypeDB::bind_method(_MD("get_radial_center_offset"), &TextureProgress::get_radial_center_offset); + + ObjectTypeDB::bind_method(_MD("set_fill_degrees","mode"),&TextureProgress::set_fill_degrees); + ObjectTypeDB::bind_method(_MD("get_fill_degrees"), &TextureProgress::get_fill_degrees); + ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"texture/under",PROPERTY_HINT_RESOURCE_TYPE,"Texture"),_SCS("set_under_texture"),_SCS("get_under_texture")); ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"texture/over",PROPERTY_HINT_RESOURCE_TYPE,"Texture"),_SCS("set_over_texture"),_SCS("get_over_texture")); ADD_PROPERTY( PropertyInfo(Variant::OBJECT,"texture/progress",PROPERTY_HINT_RESOURCE_TYPE,"Texture"),_SCS("set_progress_texture"),_SCS("get_progress_texture")); + ADD_PROPERTYNZ( PropertyInfo(Variant::INT,"mode",PROPERTY_HINT_ENUM,"Left to Right,Right to Left,Top to Bottom,Bottom to Top,Clockwise,Counter Clockwise"),_SCS("set_fill_mode"),_SCS("get_fill_mode")); + ADD_PROPERTYNZ( PropertyInfo(Variant::REAL,"radial_fill/initial_angle",PROPERTY_HINT_RANGE,"0.0,360.0,0.1,slider"),_SCS("set_radial_initial_angle"),_SCS("get_radial_initial_angle")); + ADD_PROPERTYNZ( PropertyInfo(Variant::REAL,"radial_fill/fill_degrees",PROPERTY_HINT_RANGE,"0.0,360.0,0.1,slider"),_SCS("set_fill_degrees"),_SCS("get_fill_degrees")); + ADD_PROPERTY( PropertyInfo(Variant::VECTOR2,"radial_fill/center_offset"),_SCS("set_radial_center_offset"),_SCS("get_radial_center_offset")); + + BIND_CONSTANT( FILL_LEFT_TO_RIGHT ); + BIND_CONSTANT( FILL_RIGHT_TO_LEFT ); + BIND_CONSTANT( FILL_TOP_TO_BOTTOM ); + BIND_CONSTANT( FILL_BOTTOM_TO_TOP ); + BIND_CONSTANT( FILL_CLOCKWISE ); + BIND_CONSTANT( FILL_COUNTER_CLOCKWISE ); } TextureProgress::TextureProgress() { + mode=FILL_LEFT_TO_RIGHT; + rad_center_off=Point2(); + rad_max_degrees=360; } diff --git a/scene/gui/texture_progress.h b/scene/gui/texture_progress.h index 93a0d1046c..7187fd5f07 100644 --- a/scene/gui/texture_progress.h +++ b/scene/gui/texture_progress.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -45,6 +45,27 @@ protected: void _notification(int p_what); public: + enum FillMode { + FILL_LEFT_TO_RIGHT=0, + FILL_RIGHT_TO_LEFT, + FILL_TOP_TO_BOTTOM, + FILL_BOTTOM_TO_TOP, + FILL_CLOCKWISE, + FILL_COUNTER_CLOCKWISE + }; + + void set_fill_mode(int p_fill); + int get_fill_mode(); + + void set_radial_initial_angle(float p_angle); + float get_radial_initial_angle(); + + void set_fill_degrees(float p_angle); + float get_fill_degrees(); + + void set_radial_center_offset(const Point2 &p_off); + Point2 get_radial_center_offset(); + void set_under_texture(const Ref<Texture>& p_texture); Ref<Texture> get_under_texture() const; @@ -57,6 +78,16 @@ public: Size2 get_minimum_size() const; TextureProgress(); + +private: + + FillMode mode; + float rad_init_angle; + float rad_max_degrees; + Point2 rad_center_off; + + Point2 unit_val_to_uv(float val); + Point2 get_relative_center(); }; #endif // TEXTURE_PROGRESS_H diff --git a/scene/gui/tool_button.cpp b/scene/gui/tool_button.cpp index e0ba30a202..d5bcb73476 100644 --- a/scene/gui/tool_button.cpp +++ b/scene/gui/tool_button.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/tool_button.h b/scene/gui/tool_button.h index 9b1664c3fb..648d776b51 100644 --- a/scene/gui/tool_button.h +++ b/scene/gui/tool_button.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp index b7b52a39dc..16a12fe407 100644 --- a/scene/gui/tree.cpp +++ b/scene/gui/tree.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,7 +31,7 @@ #include "os/os.h" #include "os/keyboard.h" #include "globals.h" - +#include "os/input.h" @@ -70,6 +70,7 @@ Size2 TreeItem::Cell::get_icon_size() const { else return icon_region.size; } + void TreeItem::Cell::draw_icon(const RID& p_where, const Point2& p_pos, const Size2& p_size) const{ if (icon.is_null()) @@ -120,7 +121,7 @@ void TreeItem::set_cell_mode( int p_column, TreeCellMode p_mode ) { c.val=0; c.checked=false; c.icon=Ref<Texture>(); - c.text=""; + c.text=""; c.icon_max_w=0; _changed_notify(p_column); } @@ -152,9 +153,9 @@ void TreeItem::set_text(int p_column,String p_text) { ERR_FAIL_INDEX( p_column, cells.size() ); cells[p_column].text=p_text; - + if (cells[p_column].mode==TreeItem::CELL_MODE_RANGE) { - + cells[p_column].min=0; cells[p_column].max=p_text.get_slice_count(","); cells[p_column].step=0; @@ -224,7 +225,7 @@ void TreeItem::set_range(int p_column,double p_value) { p_value=cells[p_column].min; if (p_value>cells[p_column].max) p_value=cells[p_column].max; - + cells[p_column].val=p_value; _changed_notify(p_column); @@ -236,7 +237,7 @@ double TreeItem::get_range(int p_column) const { return cells[p_column].val; } - + bool TreeItem::is_range_exponential(int p_column) const { ERR_FAIL_INDEX_V( p_column, cells.size(), false); @@ -303,7 +304,7 @@ void TreeItem::set_collapsed(bool p_collapsed) { if (tree->select_mode==Tree::SELECT_MULTI) { - tree->selected_item=this; + tree->selected_item=this; emit_signal("cell_selected"); } else { @@ -336,11 +337,11 @@ TreeItem *TreeItem::get_prev() { if (!parent || parent->childs==this) return NULL; - + TreeItem *prev = parent->childs; while(prev && prev->next!=this) prev=prev->next; - + return prev; } @@ -635,14 +636,14 @@ void TreeItem::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_range","column"),&TreeItem::get_range); ObjectTypeDB::bind_method(_MD("set_range_config","column","min","max","step","expr"),&TreeItem::set_range_config,DEFVAL(false)); ObjectTypeDB::bind_method(_MD("get_range_config","column"),&TreeItem::_get_range_config); - + ObjectTypeDB::bind_method(_MD("set_metadata","column","meta"),&TreeItem::set_metadata); ObjectTypeDB::bind_method(_MD("get_metadata","column"),&TreeItem::get_metadata); ObjectTypeDB::bind_method(_MD("set_custom_draw","column","object","callback"),&TreeItem::set_custom_draw); ObjectTypeDB::bind_method(_MD("set_collapsed","enable"),&TreeItem::set_collapsed); - ObjectTypeDB::bind_method(_MD("is_collapsed"),&TreeItem::is_collapsed); + ObjectTypeDB::bind_method(_MD("is_collapsed"),&TreeItem::is_collapsed); ObjectTypeDB::bind_method(_MD("get_next:TreeItem"),&TreeItem::get_next); ObjectTypeDB::bind_method(_MD("get_prev:TreeItem"),&TreeItem::get_prev); @@ -653,17 +654,17 @@ void TreeItem::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_prev_visible:TreeItem"),&TreeItem::get_prev_visible); ObjectTypeDB::bind_method(_MD("remove_child:TreeItem","child"),&TreeItem::_remove_child); - + ObjectTypeDB::bind_method(_MD("set_selectable","column","selectable"),&TreeItem::set_selectable); ObjectTypeDB::bind_method(_MD("is_selectable","column"),&TreeItem::is_selectable); ObjectTypeDB::bind_method(_MD("is_selected","column"),&TreeItem::is_selected); ObjectTypeDB::bind_method(_MD("select","column"),&TreeItem::select); ObjectTypeDB::bind_method(_MD("deselect","column"),&TreeItem::deselect); - + ObjectTypeDB::bind_method(_MD("set_editable","column","enabled"),&TreeItem::set_editable); ObjectTypeDB::bind_method(_MD("is_editable","column"),&TreeItem::is_editable); - + ObjectTypeDB::bind_method(_MD("set_custom_color","column","color"),&TreeItem::set_custom_color); ObjectTypeDB::bind_method(_MD("clear_custom_color","column"),&TreeItem::clear_custom_color); @@ -687,7 +688,7 @@ void TreeItem::_bind_methods() { BIND_CONSTANT( CELL_MODE_RANGE ); BIND_CONSTANT( CELL_MODE_ICON ); BIND_CONSTANT( CELL_MODE_CUSTOM ); - + } @@ -728,14 +729,20 @@ TreeItem::~TreeItem() { tree->root=0; } - if (tree && tree->popup_edited_item==this) + if (tree && tree->popup_edited_item==this) { tree->popup_edited_item=NULL; + tree->pressing_for_editor=false; + + } if (tree && tree->selected_item==this) tree->selected_item=NULL; - if (tree && tree->edited_item==this) + if (tree && tree->edited_item==this) { tree->edited_item=NULL; + tree->pressing_for_editor=false; + } + } @@ -767,7 +774,7 @@ void Tree::update_cache() { cache.arrow =get_icon("arrow"); cache.select_arrow =get_icon("select_arrow"); cache.updown=get_icon("updown"); - + cache.font_color=get_color("font_color"); cache.font_color_selected=get_color("font_color_selected"); cache.guide_color=get_color("guide_color"); @@ -795,10 +802,10 @@ int Tree::compute_item_height(TreeItem *p_item) const { if (p_item==root && hide_root) return 0; - + int height=cache.font->get_height(); - + for (int i=0;i<columns.size();i++) { @@ -812,23 +819,23 @@ int Tree::compute_item_height(TreeItem *p_item) const { } switch(p_item->cells[i].mode) { - + case TreeItem::CELL_MODE_CHECK: { - + int check_icon_h = cache.checked->get_height(); if (height<check_icon_h) height=check_icon_h; - - - + + + } case TreeItem::CELL_MODE_STRING: case TreeItem::CELL_MODE_CUSTOM: case TreeItem::CELL_MODE_ICON: { - + Ref<Texture> icon = p_item->cells[i].icon; if (!icon.is_null()) { - + Size2i s = p_item->cells[i].get_icon_size(); if (p_item->cells[i].icon_max_w>0 && s.width > p_item->cells[i].icon_max_w ) { s.height=s.height * p_item->cells[i].icon_max_w / s.width; @@ -836,15 +843,15 @@ int Tree::compute_item_height(TreeItem *p_item) const { if (s.height > height ) height=s.height; } - + } break; default: {} } } - - + + height += cache.vseparation; - + return height; } @@ -920,7 +927,7 @@ void Tree::draw_item_text(String p_text,const Ref<Texture>& p_icon,int p_icon_ma p_rect.size.x-=Math::floor(p_rect.size.y/2); Ref<Font> font = cache.font; - + p_rect.pos.y+=Math::floor((p_rect.size.y-font->get_height())/2.0) +font->get_ascent(); font->draw(ci,p_rect.pos,p_text,p_color,p_rect.size.x); } @@ -943,22 +950,24 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2& Point2i guide_from; - bool skip=(p_item==root && hide_root); + bool skip=(p_item==root && hide_root); // printf("skip (%p == %p && %i) %i\n",p_item,root,hide_root,skip); if (!skip && (p_pos.y+label_h-cache.offset.y)>0) { - // printf("entering\n"); + // printf("entering\n"); int height=label_h; Point2i guide_space=Point2i( cache.guide_width , height ); - if (p_item->childs) { //has childs, draw the guide box + + + if (!hide_folding && p_item->childs) { //has childs, draw the guide box Ref<Texture> arrow; - + if (p_item->collapsed) { arrow=cache.arrow_collapsed; @@ -976,10 +985,10 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2& // if (p_item->get_parent()!=root || !hide_root) Ref<Font> font = cache.font; - + int font_ascent=font->get_ascent(); - int ofs = p_pos.x + cache.item_margin; + int ofs = p_pos.x + (hide_folding?cache.hseparation:cache.item_margin); for (int i=0;i<columns.size();i++) { int w = get_column_width(i); @@ -1055,11 +1064,14 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2& if (p_item->cells[i].custom_bg_color) { - VisualServer::get_singleton()->canvas_item_add_rect(ci,cell_rect,p_item->cells[i].bg_color); + Rect2 r=cell_rect; + r.pos.x-=cache.hseparation; + r.size.x+=cache.hseparation; + VisualServer::get_singleton()->canvas_item_add_rect(ci,r,p_item->cells[i].bg_color); } Color col=p_item->cells[i].custom_color?p_item->cells[i].color:get_color( p_item->cells[i].selected?"font_color_selected":"font_color"); - + Point2i text_pos=item_rect.pos; text_pos.y+=Math::floor((item_rect.size.y-font->get_height())/2) + font_ascent; @@ -1097,7 +1109,7 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2& } break; case TreeItem::CELL_MODE_RANGE: { - + if (p_item->cells[i].text!="") { if (!p_item->cells[i].editable) @@ -1106,7 +1118,7 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2& int option = (int)p_item->cells[i].val; String s = p_item->cells[i].text; - s=s.get_slice(",",option); + s=s.get_slicec(',',option); Ref<Texture> downarrow = cache.select_arrow; @@ -1121,7 +1133,7 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2& } else { Ref<Texture> updown = cache.updown; - + String valtext = String::num( p_item->cells[i].val, Math::decimals( p_item->cells[i].step ) ); font->draw( ci, text_pos, valtext, col, item_rect.size.x-updown->get_width()); @@ -1178,7 +1190,7 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2& Rect2i ir=item_rect; ir.size.width-=downarrow->get_width(); draw_item_rect(p_item->cells[i],ir,col); - + Point2i arrow_pos=item_rect.pos; arrow_pos.x+=item_rect.size.x-downarrow->get_width(); arrow_pos.y+=Math::floor(((item_rect.size.y-downarrow->get_height()))/2.0); @@ -1220,7 +1232,7 @@ int Tree::draw_item(const Point2i& p_pos,const Point2& p_draw_ofs, const Size2& children_pos.x+=cache.item_margin; htotal+=label_h; children_pos.y+=htotal; - + } @@ -1265,12 +1277,12 @@ void Tree::select_single_item(TreeItem *p_selected,TreeItem *p_current,int p_col continue; if (select_mode==SELECT_ROW) { - + if (p_selected==p_current) { - + if (!c.selected) { - + c.selected=true; selected_item=p_selected; selected_col=0; @@ -1279,28 +1291,29 @@ void Tree::select_single_item(TreeItem *p_selected,TreeItem *p_current,int p_col //if (p_col==i) // p_current->selected_signal.call(p_col); } - + } else { - + if (c.selected) { - - c.selected=false; + + c.selected=false; //p_current->deselected_signal.call(p_col); } - + } - + } else if (select_mode==SELECT_SINGLE || select_mode==SELECT_MULTI) { - - if (&selected_cell==&c) { - + + if (!r_in_range && &selected_cell==&c) { + if (!selected_cell.selected) { - + selected_cell.selected=true; - + selected_item=p_selected; selected_col=i; + emit_signal("cell_selected"); if (select_mode==SELECT_MULTI) emit_signal("multi_selected",p_current,i,true); @@ -1313,10 +1326,11 @@ void Tree::select_single_item(TreeItem *p_selected,TreeItem *p_current,int p_col } } else { - + if (r_in_range && *r_in_range) { + if (!c.selected && c.selectable) { c.selected=true; emit_signal("multi_selected",p_current,i,true); @@ -1363,11 +1377,11 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_ int item_h=compute_item_height( p_item )+cache.vseparation; bool skip=(p_item==root && hide_root); - + if (!skip && p_pos.y<item_h) { // check event! - if (p_pos.x >=x_ofs && p_pos.x < (x_ofs+cache.item_margin) ) { + if (!hide_folding && (p_pos.x >=x_ofs && p_pos.x < (x_ofs+cache.item_margin) )) { if (p_item->childs) @@ -1375,7 +1389,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_ return -1; //handled! } - + int x=p_pos.x; /* find clicked column */ int col=-1; @@ -1394,7 +1408,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_ break; } - + if (col==-1) return -1; @@ -1467,7 +1481,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_ if (select_mode==SELECT_MULTI && p_mod.shift && selected_item && selected_item!=p_item) { bool inrange=false; - print_line("SELECT MULTI AND SHIFT AND ALL"); + select_single_item( p_item, root, col,selected_item,&inrange ); } else { select_single_item( p_item, root, col ); @@ -1478,8 +1492,8 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_ //} update(); } - - + + } } @@ -1490,7 +1504,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_ /* editing */ - bool bring_up_editor=c.selected && already_selected; + bool bring_up_editor=c.selected;// && already_selected; bool bring_up_value_editor=false; String editor_text=c.text; @@ -1505,14 +1519,14 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_ } break; case TreeItem::CELL_MODE_CHECK: { - + Ref<Texture> checked = cache.checked; bring_up_editor=false; //checkboxes are not edited with editor if (x>=0 && x<= checked->get_width()+cache.hseparation ) { - + p_item->set_checked(col,!c.checked); - item_edited(col,p_item); + item_edited(col,p_item); click_handled=true; //p_item->edited_signal.call(col); } @@ -1527,7 +1541,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_ popup_menu->clear(); for (int i=0;i<c.text.get_slice_count(",");i++) { - String s = c.text.get_slice(",",i); + String s = c.text.get_slicec(',',i); popup_menu->add_item(s,i); } @@ -1540,37 +1554,37 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_ //} bring_up_editor=false; } else { - + Ref<Texture> updown = cache.updown; - - + + if (x >= (col_width-item_h/2)) { - + /* touching the combo */ bool up=p_pos.y < (item_h /2); - + if (p_button==BUTTON_LEFT) { p_item->set_range( col, c.val + (up?1.0:-1.0) * c.step ); - - item_edited(col,p_item); + + item_edited(col,p_item); } else if (p_button==BUTTON_RIGHT) { - + p_item->set_range( col, (up?c.max:c.min) ); - item_edited(col,p_item); + item_edited(col,p_item); } else if (p_button==BUTTON_WHEEL_UP) { - + p_item->set_range( col, c.val + c.step ); - item_edited(col,p_item); + item_edited(col,p_item); } else if (p_button==BUTTON_WHEEL_DOWN) { - + p_item->set_range( col, c.val - c.step ); - item_edited(col,p_item); + item_edited(col,p_item); } - - //p_item->edited_signal.call(col); + + //p_item->edited_signal.call(col); bring_up_editor=false; - - + + } else { editor_text=String::num( p_item->cells[col].val, Math::decimals( p_item->cells[col].step ) ); @@ -1578,7 +1592,7 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_ if (select_mode==SELECT_MULTI && get_tree()->get_last_event_id() == focus_in_id) bring_up_editor=false; - } + } } click_handled=true; @@ -1589,12 +1603,12 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_ } break; case TreeItem::CELL_MODE_CUSTOM: { edited_item=p_item; - edited_col=col; + edited_col=col; custom_popup_rect=Rect2i(get_global_pos() + Point2i(col_ofs,_get_title_button_height()+y_ofs+item_h-cache.offset.y), Size2(get_column_width(col),item_h)); emit_signal("custom_popup_edited",((bool)(x >= (col_width-item_h/2)))); bring_up_editor=false; - item_edited(col,p_item); + item_edited(col,p_item); click_handled=true; return -1; } break; @@ -1605,44 +1619,27 @@ int Tree::propagate_mouse_event(const Point2i &p_pos,int x_ofs,int y_ofs,bool p_ return -1; + click_handled=true; popup_edited_item=p_item; popup_edited_item_col=col; - text_editor->set_pos(get_global_pos() + Point2i(col_ofs,_get_title_button_height()+y_ofs)-cache.offset ); - text_editor->set_size( Size2(col_width,item_h)); - text_editor->clear(); - text_editor->set_text( editor_text ); - text_editor->select_all(); - if (bring_up_value_editor) { - - value_editor->set_pos(get_global_pos() + Point2i(col_ofs,_get_title_button_height()+y_ofs)-cache.offset+Point2i(0,text_editor->get_size().height) ); - value_editor->set_size( Size2(col_width,1)); - value_editor->show_modal(); - updating_value_editor=true; - value_editor->set_min( c.min ); - value_editor->set_max( c.max ); - value_editor->set_step( c.step ); - value_editor->set_val( c.val ); - value_editor->set_exp_unit_value( c.expr ); - updating_value_editor=false; - } - - text_editor->show_modal(); - text_editor->grab_focus(); + pressing_item_rect=Rect2(get_global_pos() + Point2i(col_ofs,_get_title_button_height()+y_ofs)-cache.offset,Size2(col_width,item_h)); + pressing_for_editor_text=editor_text; + pressing_for_editor=true; return -1; //select } else { Point2i new_pos=p_pos; - + if (!skip) { x_ofs+=cache.item_margin; //new_pos.x-=cache.item_margin; y_ofs+=item_h; new_pos.y-=item_h; } - + if (!p_item->collapsed) { /* if not collapsed, check the childs */ @@ -1705,7 +1702,7 @@ void Tree::text_editor_enter(String p_text) { default: { ERR_FAIL(); } } - item_edited(popup_edited_item_col,popup_edited_item); + item_edited(popup_edited_item_col,popup_edited_item); update(); } @@ -1733,19 +1730,19 @@ void Tree::popup_select(int p_option) { if (popup_edited_item_col<0 || popup_edited_item_col>columns.size()) return; - + popup_edited_item->cells[popup_edited_item_col].val=p_option; //popup_edited_item->edited_signal.call( popup_edited_item_col ); update(); - item_edited(popup_edited_item_col,popup_edited_item); + item_edited(popup_edited_item_col,popup_edited_item); } void Tree::_input_event(InputEvent p_event) { - + switch (p_event.type) { - + case InputEvent::KEY: { if (!p_event.key.pressed) @@ -2062,6 +2059,33 @@ void Tree::_input_event(InputEvent p_event) { update(); } + if (pressing_for_editor && popup_edited_item && popup_edited_item->get_cell_mode(popup_edited_item_col)==TreeItem::CELL_MODE_RANGE) { + //range drag + + if (!range_drag_enabled) { + + Vector2 cpos = Vector2(b.x,b.y); + if (cpos.distance_to(pressing_pos)>2) { + range_drag_enabled=true; + range_drag_capture_pos=cpos; + range_drag_base=popup_edited_item->get_range(popup_edited_item_col); + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_CAPTURED); + } + } else { + + TreeItem::Cell &c=popup_edited_item->cells[popup_edited_item_col]; + float diff_y = -b.relative_y; + diff_y=Math::pow(ABS(diff_y),1.8)*SGN(diff_y); + diff_y*=0.1; + range_drag_base=CLAMP(range_drag_base + c.step * diff_y, c.min, c.max); + + popup_edited_item->set_range(popup_edited_item_col,range_drag_base); + item_edited(popup_edited_item_col,popup_edited_item); + + } + + } + if (drag_touching && ! drag_touching_deaccel) { @@ -2072,18 +2096,43 @@ void Tree::_input_event(InputEvent p_event) { } } break; case InputEvent::MOUSE_BUTTON: { - + if (cache.font.is_null()) // avoid a strange case that may fuckup stuff update_cache(); - const InputEventMouseButton& b=p_event.mouse_button; + const InputEventMouseButton& b=p_event.mouse_button; if (!b.pressed) { if (b.button_index==BUTTON_LEFT) { + if (pressing_for_editor) { + + if (range_drag_enabled) { + + range_drag_enabled=false; + Input::get_singleton()->set_mouse_mode(Input::MOUSE_MODE_VISIBLE); + warp_mouse(range_drag_capture_pos); + } else { + text_editor->set_pos(pressing_item_rect.pos); + text_editor->set_size(pressing_item_rect.size); + + text_editor->clear(); + text_editor->set_text( pressing_for_editor_text ); + text_editor->select_all(); + + text_editor->show_modal(); + text_editor->grab_focus(); + + } + pressing_for_editor=false; + + } + + + if (cache.click_type==Cache::CLICK_BUTTON) { emit_signal("button_pressed",cache.click_item,cache.click_column,cache.click_id); @@ -2116,7 +2165,7 @@ void Tree::_input_event(InputEvent p_event) { switch(b.button_index) { case BUTTON_LEFT: { Ref<StyleBox> bg = cache.bg; - + Point2 pos = Point2(b.x,b.y) - bg->get_offset(); cache.click_type=Cache::CLICK_NONE; if (show_column_titles) { @@ -2145,11 +2194,15 @@ void Tree::_input_event(InputEvent p_event) { break; click_handled=false; + pressing_for_editor=false; blocked++; bool handled = propagate_mouse_event(pos+cache.offset,0,0,b.doubleclick,root,b.button_index,b.mod); blocked--; + if (pressing_for_editor) { + pressing_pos=Point2(b.x,b.y); + } if (drag_touching) { @@ -2174,18 +2227,18 @@ void Tree::_input_event(InputEvent p_event) { } break; - case BUTTON_WHEEL_UP: { + case BUTTON_WHEEL_UP: { v_scroll->set_val( v_scroll->get_val()-v_scroll->get_page()/8 ); } break; case BUTTON_WHEEL_DOWN: { - + v_scroll->set_val( v_scroll->get_val()+v_scroll->get_page()/8 ); } break; } - + } break; } - + } @@ -2218,9 +2271,12 @@ bool Tree::edit_selected() { TreeItem::Cell &c = s->cells[col]; + if (c.mode==TreeItem::CELL_MODE_CHECK) { - - if (c.mode==TreeItem::CELL_MODE_CUSTOM) { + s->set_checked(col, !c.checked); + item_edited(col,s); + return true; + } else if (c.mode==TreeItem::CELL_MODE_CUSTOM) { edited_item=s; edited_col=col; @@ -2234,7 +2290,7 @@ bool Tree::edit_selected() { popup_menu->clear(); for (int i=0;i<c.text.get_slice_count(",");i++) { - String s = c.text.get_slice(",",i); + String s = c.text.get_slicec(',',i); popup_menu->add_item(s,i); } @@ -2283,10 +2339,10 @@ Size2 Tree::get_internal_min_size() const { if (root) size.height+=get_item_height(root); for (int i=0;i<columns.size();i++) { - + size.width+=columns[i].min_width; } - + return size; } @@ -2305,39 +2361,39 @@ void Tree::update_scrollbars() { Size2 vmin = v_scroll->get_combined_minimum_size(); - + v_scroll->set_begin( Point2(size.width - vmin.width , cache.bg->get_margin(MARGIN_TOP)) ); v_scroll->set_end( Point2(size.width, size.height-cache.bg->get_margin(MARGIN_TOP)-cache.bg->get_margin(MARGIN_BOTTOM)) ); - + h_scroll->set_begin( Point2( 0, size.height - hmin.height) ); h_scroll->set_end( Point2(size.width-vmin.width, size.height) ); - - + + Size2 min = get_internal_min_size(); - + if (min.height < size.height - hmin.height) { - + v_scroll->hide(); cache.offset.y=0; } else { - + v_scroll->show(); v_scroll->set_max(min.height); v_scroll->set_page(size.height - hmin.height - tbh); cache.offset.y=v_scroll->get_val(); } - + if (min.width < size.width - vmin.width) { - + h_scroll->hide(); cache.offset.x=0; } else { - + h_scroll->show(); h_scroll->set_max(min.width); h_scroll->set_page(size.width - vmin.width); cache.offset.x=h_scroll->get_val(); - } + } } @@ -2360,6 +2416,11 @@ void Tree::_notification(int p_what) { } } + if (p_what==NOTIFICATION_VISIBILITY_CHANGED) { + + drag_touching=false; + } + if (p_what==NOTIFICATION_ENTER_TREE) { update_cache();; @@ -2411,16 +2472,16 @@ void Tree::_notification(int p_what) { } if (p_what==NOTIFICATION_DRAW) { - + update_cache(); update_scrollbars(); - RID ci = get_canvas_item(); - + RID ci = get_canvas_item(); + VisualServer::get_singleton()->canvas_item_set_clip(ci,true); - + Ref<StyleBox> bg = cache.bg; Ref<StyleBox> bg_focus = get_stylebox("bg_focus"); - + Point2 draw_ofs; draw_ofs+=bg->get_offset(); Size2 draw_size=get_size()-bg->get_minimum_size(); @@ -2438,7 +2499,7 @@ void Tree::_notification(int p_what) { draw_size.y-=tbh; if (root) { - + draw_item( Point2(),draw_ofs,draw_size,root); @@ -2449,10 +2510,10 @@ void Tree::_notification(int p_what) { // int size_y=exposed.size.height-bg->get_minimum_size().height; for (int i=0;i<(columns.size()-1-1);i++) { - + ofs+=get_column_width(i); //get_painter()->draw_fill_rect( Point2(ofs+cache.hseparation/2, from_y), Size2( 1, size_y ),color( COLOR_TREE_GRID) ); - } + } if (show_column_titles) { @@ -2472,6 +2533,10 @@ void Tree::_notification(int p_what) { } } + if (p_what==NOTIFICATION_THEME_CHANGED) { + update_cache(); + } + } @@ -2548,7 +2613,7 @@ TreeItem* Tree::get_last_item() { } void Tree::item_edited(int p_column,TreeItem *p_item) { - + edited_item=p_item; edited_col=p_column; emit_signal("item_edited"); @@ -2556,14 +2621,14 @@ void Tree::item_edited(int p_column,TreeItem *p_item) { void Tree::item_changed(int p_column,TreeItem *p_item) { - update(); + update(); } void Tree::item_selected(int p_column,TreeItem *p_item) { if (select_mode==SELECT_MULTI) { - + if (!p_item->cells[p_column].selectable) return; @@ -2579,16 +2644,16 @@ void Tree::item_selected(int p_column,TreeItem *p_item) { void Tree::item_deselected(int p_column,TreeItem *p_item) { if (select_mode==SELECT_MULTI) { - + p_item->cells[p_column].selected=false; - } + } update(); } void Tree::set_select_mode(SelectMode p_mode) { - select_mode=p_mode; + select_mode=p_mode; } void Tree::clear() { @@ -2606,6 +2671,8 @@ void Tree::clear() { selected_item=NULL; edited_item=NULL; popup_edited_item=NULL; + selected_item=NULL; + pressing_for_editor=false; update(); }; @@ -2616,15 +2683,15 @@ void Tree::set_hide_root(bool p_enabled) { - hide_root=p_enabled; - update(); + hide_root=p_enabled; + update(); } void Tree::set_column_min_width(int p_column,int p_min_width) { ERR_FAIL_INDEX(p_column,columns.size()); - + if (p_min_width<1) return; columns[p_column].min_width=p_min_width; @@ -2634,8 +2701,8 @@ void Tree::set_column_min_width(int p_column,int p_min_width) { void Tree::set_column_expand(int p_column,bool p_expand) { ERR_FAIL_INDEX(p_column,columns.size()); - - columns[p_column].expand=p_expand; + + columns[p_column].expand=p_expand; update(); } @@ -2650,78 +2717,78 @@ int Tree::get_selected_column() const { } TreeItem *Tree::get_edited() const { - + return edited_item; } int Tree::get_edited_column() const { - + return edited_col; } TreeItem* Tree::get_next_selected( TreeItem* p_item) { - + //if (!p_item) // return NULL; if (!root) - return NULL; - + return NULL; + while(true) { - - + + if (!p_item) { p_item=root; } else { - + if (p_item->childs) { - + p_item=p_item->childs; - + } else if (p_item->next) { - - p_item=p_item->next; + + p_item=p_item->next; } else { - + while(!p_item->next) { - + p_item=p_item->parent; if (p_item==NULL) return NULL; } - + p_item=p_item->next; } - + } - + for (int i=0;i<columns.size();i++) if (p_item->cells[i].selected) return p_item; } - + return NULL; } int Tree::get_column_width(int p_column) const { - + ERR_FAIL_INDEX_V(p_column,columns.size(),-1); - - + + if (!columns[p_column].expand) return columns[p_column].min_width; - + Ref<StyleBox> bg = cache.bg; - + int expand_area=get_size().width-(bg->get_margin(MARGIN_LEFT)+bg->get_margin(MARGIN_RIGHT)); - + if (v_scroll->is_visible()) expand_area-=v_scroll->get_combined_minimum_size().width; - + int expanding_columns=0; int expanding_total=0; - + for (int i=0;i<columns.size();i++) { - + if (!columns[i].expand) { expand_area-=columns[i].min_width; } else { @@ -2732,30 +2799,30 @@ int Tree::get_column_width(int p_column) const { if (expand_area<expanding_total) return columns[p_column].min_width; - + ERR_FAIL_COND_V(expanding_columns==0,-1); // shouldnt happen - + return expand_area * columns[p_column].min_width / expanding_total; } void Tree::propagate_set_columns(TreeItem *p_item) { - + p_item->cells.resize( columns.size() ); - + TreeItem *c = p_item->get_children(); while(c) { - + propagate_set_columns(c); c=c->get_next(); } } void Tree::set_columns(int p_columns) { - + ERR_FAIL_COND(p_columns<1); ERR_FAIL_COND(blocked>0); columns.resize(p_columns); - + if (root) propagate_set_columns(root); if (selected_col>=p_columns) @@ -2765,17 +2832,17 @@ void Tree::set_columns(int p_columns) { } int Tree::get_columns() const { - + return columns.size(); } void Tree::_scroll_moved(float) { - + update(); } Rect2 Tree::get_custom_popup_rect() const { - + return custom_popup_rect; } @@ -3055,9 +3122,19 @@ bool Tree::can_cursor_exit_tree() const { return cursor_can_exit_tree; } +void Tree::set_hide_folding(bool p_hide) { + hide_folding=p_hide; + update(); +} + +bool Tree::is_folding_hidden() const { + + return hide_folding; +} + void Tree::_bind_methods() { - + ObjectTypeDB::bind_method(_MD("_input_event"),&Tree::_input_event); ObjectTypeDB::bind_method(_MD("_popup_select"),&Tree::popup_select); ObjectTypeDB::bind_method(_MD("_text_editor_enter"),&Tree::text_editor_enter); @@ -3081,7 +3158,7 @@ void Tree::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_columns","amount"),&Tree::set_columns); ObjectTypeDB::bind_method(_MD("get_columns"),&Tree::get_columns); - + ObjectTypeDB::bind_method(_MD("get_edited:TreeItem"),&Tree::get_edited); ObjectTypeDB::bind_method(_MD("get_edited_column"),&Tree::get_edited_column); ObjectTypeDB::bind_method(_MD("get_custom_popup_rect"),&Tree::get_custom_popup_rect); @@ -3096,6 +3173,9 @@ void Tree::_bind_methods() { ObjectTypeDB::bind_method(_MD("get_column_title","column"),&Tree::get_column_title); ObjectTypeDB::bind_method(_MD("get_scroll"),&Tree::get_scroll); + ObjectTypeDB::bind_method(_MD("set_hide_folding","hide"),&Tree::set_hide_folding); + ObjectTypeDB::bind_method(_MD("is_folding_hidden"),&Tree::is_folding_hidden); + ADD_SIGNAL( MethodInfo("item_selected")); ADD_SIGNAL( MethodInfo("cell_selected")); @@ -3120,7 +3200,7 @@ Tree::Tree() { edited_item=NULL; selected_col=-1; edited_col=-1; - + hide_root=false; select_mode=SELECT_SINGLE; root=0; @@ -3128,8 +3208,8 @@ Tree::Tree() { popup_edited_item=NULL; text_editor=NULL; set_focus_mode(FOCUS_ALL); - - + + popup_menu = memnew( PopupMenu ); popup_menu->hide(); add_child(popup_menu); @@ -3145,7 +3225,7 @@ Tree::Tree() { h_scroll = memnew( HScrollBar ); v_scroll = memnew( VScrollBar ); - + add_child(h_scroll); add_child(v_scroll); @@ -3180,6 +3260,10 @@ Tree::Tree() { drag_speed=0; drag_touching=false; drag_touching_deaccel=false; + pressing_for_editor=false; + range_drag_enabled=false; + + hide_folding=false; } @@ -3189,6 +3273,6 @@ Tree::~Tree() { if (root) { memdelete( root ); } - + } diff --git a/scene/gui/tree.h b/scene/gui/tree.h index 3ffbececb2..8fb9b802a1 100644 --- a/scene/gui/tree.h +++ b/scene/gui/tree.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -228,6 +228,7 @@ public: void set_tooltip(int p_column, const String& p_tooltip); String get_tooltip(int p_column) const; + void clear_children(); void move_to_top(); @@ -258,7 +259,18 @@ friend class TreeItem; TreeItem *popup_edited_item; TreeItem *selected_item; TreeItem *edited_item; + + int pressed_button; + bool pressing_for_editor; + String pressing_for_editor_text; + Vector2 pressing_pos; + Rect2 pressing_item_rect; + + float range_drag_base; + bool range_drag_enabled; + Vector2 range_drag_capture_pos; + //TreeItem *cursor_item; //int cursor_column; @@ -399,6 +411,8 @@ friend class TreeItem; bool drag_touching_deaccel; bool click_handled; + bool hide_folding; + protected: static void _bind_methods(); @@ -454,6 +468,13 @@ public: void set_cursor_can_exit_tree(bool p_enable); bool can_cursor_exit_tree() const; + VScrollBar *get_vscroll_bar() { return v_scroll; } + + void set_hide_folding(bool p_hide); + bool is_folding_hidden() const; + + + Tree(); ~Tree(); diff --git a/scene/gui/video_player.cpp b/scene/gui/video_player.cpp index 050fd890f4..9b9c797ed9 100644 --- a/scene/gui/video_player.cpp +++ b/scene/gui/video_player.cpp @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -28,6 +28,88 @@ /*************************************************************************/ #include "video_player.h" + + +int VideoPlayer::InternalStream::get_channel_count() const { + + return player->sp_get_channel_count(); +} +void VideoPlayer::InternalStream::set_mix_rate(int p_rate){ + + return player->sp_set_mix_rate(p_rate); +} +bool VideoPlayer::InternalStream::mix(int32_t *p_buffer,int p_frames){ + + return player->sp_mix(p_buffer,p_frames); +} +void VideoPlayer::InternalStream::update(){ + + player->sp_update(); +} + + +int VideoPlayer::sp_get_channel_count() const { + + return playback->get_channels(); +} + +void VideoPlayer::sp_set_mix_rate(int p_rate){ + + server_mix_rate=p_rate; +} + +bool VideoPlayer::sp_mix(int32_t *p_buffer,int p_frames) { + + if (resampler.is_ready()) { + return resampler.mix(p_buffer,p_frames); + } + + return false; +} + +void VideoPlayer::sp_update() { +#if 0 + _THREAD_SAFE_METHOD_ + //update is unused + if (!paused && playback.is_valid()) { + + if (!playback->is_playing()) { + //stream depleted data, but there's still audio in the ringbuffer + //check that all this audio has been flushed before stopping the stream + int to_mix = resampler.get_total() - resampler.get_todo(); + if (to_mix==0) { + stop(); + return; + } + + return; + } + + int todo =resampler.get_todo(); + int wrote = playback->mix(resampler.get_write_buffer(),todo); + resampler.write(wrote); + } +#endif +} + +int VideoPlayer::_audio_mix_callback(void* p_udata,const int16_t *p_data,int p_frames) { + + VideoPlayer *vp=(VideoPlayer*)p_udata; + + int todo=MIN(vp->resampler.get_todo(),p_frames); + + int16_t *wb = vp->resampler.get_write_buffer(); + int c = vp->resampler.get_channel_count(); + + for(int i=0;i<todo*c;i++) { + wb[i]=p_data[i]; + } + vp->resampler.write(todo); + return todo; +} + + + void VideoPlayer::_notification(int p_notification) { switch (p_notification) { @@ -45,16 +127,25 @@ void VideoPlayer::_notification(int p_notification) { return; if (paused) return; - if (!stream->is_playing()) + if (!playback->is_playing()) return; - stream->update(get_tree()->get_idle_process_time()); - int prev_width = texture->get_width(); + double audio_time = AudioServer::get_singleton()->get_mix_time(); + + double delta = last_audio_time==0?0:audio_time-last_audio_time; + last_audio_time=audio_time; + if (delta==0) + return; + + + playback->update(delta); + + /*int prev_width = texture->get_width(); stream->pop_frame(texture); if (prev_width == 0) { update(); minimum_size_changed(); - }; + };*/ } break; @@ -75,6 +166,9 @@ void VideoPlayer::_notification(int p_notification) { }; + + + Size2 VideoPlayer::get_minimum_size() const { if (!expand && !texture.is_null()) @@ -100,15 +194,33 @@ void VideoPlayer::set_stream(const Ref<VideoStream> &p_stream) { stop(); - texture = Ref<ImageTexture>(memnew(ImageTexture)); - stream=p_stream; - if (!stream.is_null()) { - - stream->set_loop(loops); - stream->set_paused(paused); + if (stream.is_valid()) { + stream->set_audio_track(audio_track); + playback=stream->instance_playback(); + } else { + playback=Ref<VideoStreamPlayback>(); + } + + if (!playback.is_null()) { + playback->set_loop(loops); + playback->set_paused(paused); + texture=playback->get_texture(); + + AudioServer::get_singleton()->lock(); + resampler.setup(playback->get_channels(),playback->get_mix_rate(),server_mix_rate,buffering_ms,0); + AudioServer::get_singleton()->unlock(); + playback->set_mix_callback(_audio_mix_callback,this); + + } else { + texture.unref(); + AudioServer::get_singleton()->lock(); + resampler.clear(); + AudioServer::get_singleton()->unlock(); } + update(); + }; Ref<VideoStream> VideoPlayer::get_stream() const { @@ -119,36 +231,43 @@ Ref<VideoStream> VideoPlayer::get_stream() const { void VideoPlayer::play() { ERR_FAIL_COND(!is_inside_tree()); - if (stream.is_null()) + if (playback.is_null()) return; - stream->play(); + playback->stop(); + playback->play(); set_process(true); + AudioServer::get_singleton()->stream_set_active(stream_rid,true); + AudioServer::get_singleton()->stream_set_volume_scale(stream_rid,volume); + last_audio_time=0; }; void VideoPlayer::stop() { if (!is_inside_tree()) return; - if (stream.is_null()) + if (playback.is_null()) return; - stream->stop(); + playback->stop(); + AudioServer::get_singleton()->stream_set_active(stream_rid,false); + resampler.clear(); set_process(false); + last_audio_time=0; }; bool VideoPlayer::is_playing() const { - if (stream.is_null()) + if (playback.is_null()) return false; - return stream->is_playing(); + return playback->is_playing(); }; void VideoPlayer::set_paused(bool p_paused) { paused=p_paused; - if (stream.is_valid()) { - stream->set_paused(p_paused); + if (playback.is_valid()) { + playback->set_paused(p_paused); set_process(!p_paused); }; }; @@ -156,7 +275,27 @@ void VideoPlayer::set_paused(bool p_paused) { bool VideoPlayer::is_paused() const { return paused; -}; +} + +void VideoPlayer::set_buffering_msec(int p_msec) { + + buffering_ms=p_msec; +} + +int VideoPlayer::get_buffering_msec() const{ + + return buffering_ms; +} + +void VideoPlayer::set_audio_track(int p_track) { + audio_track=p_track; +} + +int VideoPlayer::get_audio_track() const { + + return audio_track; +} + void VideoPlayer::set_volume(float p_vol) { @@ -194,9 +333,9 @@ String VideoPlayer::get_stream_name() const { float VideoPlayer::get_stream_pos() const { - if (stream.is_null()) + if (playback.is_null()) return 0; - return stream->get_pos(); + return playback->get_pos(); }; @@ -229,6 +368,9 @@ void VideoPlayer::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_volume_db","db"),&VideoPlayer::set_volume_db); ObjectTypeDB::bind_method(_MD("get_volume_db"),&VideoPlayer::get_volume_db); + ObjectTypeDB::bind_method(_MD("set_audio_track","track"),&VideoPlayer::set_audio_track); + ObjectTypeDB::bind_method(_MD("get_audio_track"),&VideoPlayer::get_audio_track); + ObjectTypeDB::bind_method(_MD("get_stream_name"),&VideoPlayer::get_stream_name); ObjectTypeDB::bind_method(_MD("get_stream_pos"),&VideoPlayer::get_stream_pos); @@ -239,7 +381,10 @@ void VideoPlayer::_bind_methods() { ObjectTypeDB::bind_method(_MD("set_expand","enable"), &VideoPlayer::set_expand ); ObjectTypeDB::bind_method(_MD("has_expand"), &VideoPlayer::has_expand ); + ObjectTypeDB::bind_method(_MD("set_buffering_msec","msec"),&VideoPlayer::set_buffering_msec); + ObjectTypeDB::bind_method(_MD("get_buffering_msec"),&VideoPlayer::get_buffering_msec); + ADD_PROPERTY( PropertyInfo(Variant::INT, "stream/audio_track",PROPERTY_HINT_RANGE,"0,128,1"), _SCS("set_audio_track"), _SCS("get_audio_track") ); ADD_PROPERTY( PropertyInfo(Variant::OBJECT, "stream/stream", PROPERTY_HINT_RESOURCE_TYPE,"VideoStream"), _SCS("set_stream"), _SCS("get_stream") ); // ADD_PROPERTY( PropertyInfo(Variant::BOOL, "stream/loop"), _SCS("set_loop"), _SCS("has_loop") ); ADD_PROPERTY( PropertyInfo(Variant::REAL, "stream/volume_db", PROPERTY_HINT_RANGE,"-80,24,0.01"), _SCS("set_volume_db"), _SCS("get_volume_db") ); @@ -257,6 +402,16 @@ VideoPlayer::VideoPlayer() { autoplay = false; expand = true; loops = false; + + audio_track=0; + + buffering_ms=500; + server_mix_rate=44100; + + internal_stream.player=this; + stream_rid=AudioServer::get_singleton()->audio_stream_create(&internal_stream); + last_audio_time=0; + }; VideoPlayer::~VideoPlayer() { diff --git a/scene/gui/video_player.h b/scene/gui/video_player.h index 3eb629ced5..c485e3d6b6 100644 --- a/scene/gui/video_player.h +++ b/scene/gui/video_player.h @@ -5,7 +5,7 @@ /* GODOT ENGINE */ /* http://www.godotengine.org */ /*************************************************************************/ -/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2007-2015 Juan Linietsky, Ariel Manzur. */ /* */ /* Permission is hereby granted, free of charge, to any person obtaining */ /* a copy of this software and associated documentation files (the */ @@ -31,22 +31,50 @@ #include "scene/resources/video_stream.h" #include "scene/gui/control.h" +#include "servers/audio/audio_rb_resampler.h" class VideoPlayer : public Control { OBJ_TYPE(VideoPlayer,Control); + struct InternalStream : public AudioServer::AudioStream { + VideoPlayer *player; + virtual int get_channel_count() const; + virtual void set_mix_rate(int p_rate); //notify the stream of the mix rate + virtual bool mix(int32_t *p_buffer,int p_frames); + virtual void update(); + }; + + + InternalStream internal_stream; + Ref<VideoStreamPlayback> playback; Ref<VideoStream> stream; + + int sp_get_channel_count() const; + void sp_set_mix_rate(int p_rate); //notify the stream of the mix rate + bool sp_mix(int32_t *p_buffer,int p_frames); + void sp_update(); + + RID stream_rid; Ref<ImageTexture> texture; Image last_frame; + AudioRBResampler resampler; + bool paused; bool autoplay; float volume; + double last_audio_time; bool expand; bool loops; + int buffering_ms; + int server_mix_rate; + int audio_track; + + static int _audio_mix_callback(void* p_udata,const int16_t *p_data,int p_frames); + protected: @@ -82,6 +110,12 @@ public: void set_autoplay(bool p_vol); bool has_autoplay() const; + void set_audio_track(int p_track); + int get_audio_track() const; + + void set_buffering_msec(int p_msec); + int get_buffering_msec() const; + VideoPlayer(); ~VideoPlayer(); }; |