| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487 |
- /******************************************************************************/
- #include "stdafx.h"
- namespace EE{
- /******************************************************************************/
- #define TEXTLINE_OFFSET 0.16f // set >=0.06 (at this value cursor is aligned with the TextLine rect left edge)
- #define TEXTLINE_MARGIN 1.0f
- #define ADJUST_OFFSET_ON_SEL 0 // if adjust offset when calling select methods
- /******************************************************************************/
- // MANAGE
- /******************************************************************************/
- static void Clear(TextLine &tl) {tl.clear();}
- void TextLine::zero()
- {
- kb_lit =true;
- show_find=false;
- _can_select=false;
- _max_length=-1;
- _offset = 0;
- _func_immediate=false;
- _func_user =null;
- _func =null;
- _edit.reset();
- }
- TextLine::TextLine() {zero();}
- TextLine& TextLine::del()
- {
- reset. del();
- hint. del();
- _text. del();
- _skin.clear();
- super::del(); zero(); return T;
- }
- void TextLine::createReset()
- {
- reset.create().func(Clear, T).focusable(false).hide()._parent=this;
- reset._sub_type=BUTTON_TYPE_TEXTLINE_CLEAR;
- reset.skin=_skin;
- }
- TextLine& TextLine::create(C Str &text)
- {
- del();
- createReset();
- T._type =GO_TEXTLINE;
- T._visible =true;
- T._rect.max.x= 0.40f;
- T._rect.min.y=-0.05f;
- return set(text, QUIET);
- }
- TextLine& TextLine::create(C TextLine &src)
- {
- if(this!=&src)
- {
- if(!src.is())del();else
- {
- copyParams(src);
- _type =GO_TEXTLINE;
- kb_lit =src. kb_lit;
- show_find =src. show_find;
- hint =src. hint;
- _skin =src._skin;
- _can_select =src._can_select;
- _max_length =src._max_length;
- _offset =src._offset;
- _func_immediate=src._func_immediate;
- _func_user =src._func_user;
- _func =src._func;
- _text =src._text;
- _edit =src._edit;
- reset.create(src.reset)._parent=this;
- setTextInput();
- }
- }
- return T;
- }
- /******************************************************************************/
- // GET / SET
- /******************************************************************************/
- Bool TextLine::showClear ()C {return T().is();}
- Flt TextLine::clientWidth()C {Flt w=rect().w(); if(showClear() && reset.visible())w-=reset.rect().w(); return w;}
- C Str& TextLine::displayText()C {return password() ? Gui.passTemp(_text.length()) : _text;} // Warning: this is not thread-safe
- /******************************************************************************/
- void TextLine::setTextInput()C
- {
- #if ANDROID
- if(Gui.kb()==this)Kb.setTextInput(T(), (_edit.sel<0) ? cursor() : _edit.sel, cursor(), password());
- #endif
- }
- /******************************************************************************/
- Bool TextLine::password( )C {return _edit.password ; }
- TextLine& TextLine::password(Bool on) {if(password()!=on){_edit.password=on; setTextInput();} return T;}
- /******************************************************************************/
- TextLine& TextLine::maxLength(Int max_length)
- {
- if( max_length<0)max_length=-1;
- if(T._max_length!= max_length)
- {
- T._max_length=max_length;
- if(max_length>=0 && _text.length()>max_length)
- {
- _text.clip( max_length);
- MIN(_edit.cur, max_length);
- MIN(_edit.sel, max_length);
- setTextInput();
- }
- }
- return T;
- }
- /******************************************************************************/
- void TextLine::adjustOffset()
- {
- if(GuiSkin *skin=getSkin())
- if(TextStyle *text_style=skin->textline.text_style())
- {
- TextStyleParams ts=*text_style; ts.size=rect().h()*skin->textline.text_size;
- #if DEFAULT_FONT_FROM_CUSTOM_SKIN
- if(!ts.font())ts.font(skin->font()); // adjust font in case it's empty and the custom skin has a different font than the 'Gui.skin'
- #endif
- Flt x=ts.textWidth(displayText(), _edit.cur) + _offset + ts.size.x*TEXTLINE_OFFSET, w=clientWidth(), margin=ts.size.x*TEXTLINE_MARGIN;
- if( x< margin)_offset =Min(_offset-x+w*0.5f, 0.0f);else
- if( x>=w-margin)_offset-= x-w*0.5f;
- }
- }
- Bool TextLine::cursorChanged(Int position)
- {
- Clamp(position, 0, _text.length()); if(cursor()!=position)
- {
- _edit.cur=position;
- adjustOffset();
- return true;
- }
- return false;
- }
- TextLine& TextLine::cursor(Int position)
- {
- if(cursorChanged(position))setTextInput();
- return T;
- }
- /******************************************************************************/
- Bool TextLine::setChanged(C Str &text, SET_MODE mode)
- {
- Str t=text; if(_max_length>=0)t.clip(_max_length);
- if(!Equal(T._text, t, true))
- {
- T._text = t;
- T._edit.sel=-1;
- if(cursor()>t.length())cursorChanged(t.length());
- if(mode!=QUIET)call();
- return true;
- }
- return false;
- }
- TextLine& TextLine::set(C Str &text, SET_MODE mode)
- {
- if(setChanged(text, mode))setTextInput();
- return T;
- }
- TextLine& TextLine::clear(SET_MODE mode) {return set(S, mode);}
- /******************************************************************************/
- TextLine& TextLine::func(void (*func)(Ptr), Ptr user, Bool immediate)
- {
- T._func =func;
- T._func_user =user;
- T._func_immediate=immediate;
- return T;
- }
- void TextLine::call()
- {
- if(_func)if(_func_immediate)_func(_func_user);else Gui.addFuncCall(_func, _func_user);
- }
- /******************************************************************************/
- TextLine& TextLine::selectNone()
- {
- if(_edit.sel>=0)
- {
- _edit.sel=-1;
- setTextInput();
- }
- return T;
- }
- TextLine& TextLine::selectAll()
- {
- if(_text.is())
- if(_edit.sel!=0 || _edit.cur!=_text.length())
- {
- _edit.sel=0;
- #if ADJUST_OFFSET_ON_SEL
- cursorChanged(_text.length());
- #else
- _edit.cur=_text.length();
- #endif
- setTextInput();
- }
- return T;
- }
- TextLine& TextLine::selectExtNot()
- {
- Int dot=TextPosI(_text, '.');
- if(dot<=0)dot=_text.length();
- if(dot> 0)
- if(_edit.sel!=0 || _edit.cur!=dot)
- {
- _edit.sel=0;
- #if ADJUST_OFFSET_ON_SEL
- cursorChanged(dot);
- #else
- _edit.cur=dot;
- #endif
- setTextInput();
- }
- return T;
- }
- /******************************************************************************/
- TextLine& TextLine::rect(C Rect &rect)
- {
- //if(T.rect()!=rect) below looks fast so don't need this
- {
- super::rect(rect);
- reset .rect(Rect(rect.max.x-rect.h(), rect.min.y, rect.max.x, rect.max.y));
- }
- return T;
- }
- TextLine& TextLine::move(C Vec2 &delta)
- {
- //if(delta.any()) looks fast so skip this check
- {
- super::move(delta);
- reset .move(delta);
- }
- return T;
- }
- /******************************************************************************/
- TextLine& TextLine::skin(C GuiSkinPtr &skin, Bool sub_objects)
- {
- T._skin=skin;
- if(sub_objects)reset.skin=skin;
- return T;
- }
- /******************************************************************************/
- // MAIN
- /******************************************************************************/
- GuiObj* TextLine::test(C GuiPC &gpc, C Vec2 &pos, GuiObj* &mouse_wheel)
- {
- if(GuiObj *go=super::test(gpc, pos, mouse_wheel))
- {
- if(showClear())if(GuiObj *go=reset.test(gpc, pos, mouse_wheel))return go;
- return go;
- }
- return null;
- }
- void TextLine::update(C GuiPC &gpc)
- {
- GuiPC gpc2(gpc, visible() && showClear(), enabled());
- if( gpc2.enabled)
- {
- reset.update(gpc2);
- if(Gui.kb()==this)
- {
- #if !ADJUST_OFFSET_ON_SEL
- Int sel =_edit.sel;
- #endif
- Int cur =_edit.cur;
- Bool changed= EditText(_text, _edit);
- if( changed)
- {
- if(_max_length>=0 && _text.length()>_max_length)
- {
- _text.clip( _max_length);
- MIN(_edit.cur, _max_length);
- MIN(_edit.sel, _max_length);
- }
- call();
- }
- if(cur!=_edit.cur || changed
- #if !ADJUST_OFFSET_ON_SEL // when offset is not adjusted on selection, then it's possible that cursor is outside of visible space, so when changing selection (clearing it) we should focus on the cursor
- || sel!=_edit.sel
- #endif
- ){adjustOffset(); setTextInput();}
- }
- C Vec2 *touch_pos =null;
- Byte touch_state=0 ; if(Gui.ms()==this && (Ms._button[0]&(BS_ON|BS_PUSHED))){touch_pos=&Ms.pos(); touch_state=Ms._button[0];} if(!touch_pos)REPA(Touches)if(Touches[i].guiObj()==this && (Touches[i]._state&(BS_ON|BS_PUSHED))){touch_pos=&Touches[i].pos(); touch_state=Touches[i]._state;}
- if(_text.is() && touch_pos)
- {
- if(GuiSkin *skin=getSkin())
- if(TextStyle *text_style=skin->textline.text_style())
- {
- TextStyleParams ts=*text_style; ts.size=rect().h()*skin->textline.text_size;
- #if DEFAULT_FONT_FROM_CUSTOM_SKIN
- if(!ts.font())ts.font(skin->font()); // adjust font in case it's empty and the custom skin has a different font than the 'Gui.skin'
- #endif
- Int pos=ts.textPos(displayText(), touch_pos->x - rect().min.x - ((Gui._overlay_textline==this) ? Gui._overlay_textline_offset.x : gpc.offset.x) - _offset - ts.size.x*TEXTLINE_OFFSET, !ButtonDb(touch_state) && !_edit.overwrite);
- if(ButtonDb(touch_state))
- {
- _edit.cur=
- _edit.sel=pos;
- CHAR_TYPE type=CharType(_text[Min(pos, _text.length()-1)]);
- for(; _edit.sel && CharType(_text[_edit.sel-1])==type; _edit.sel--);
- for(; _edit.cur<_text.length() && CharType(_text[_edit.cur ])==type; _edit.cur++);
- if ( _edit.sel==_edit.cur)_edit.sel=-1;
- _can_select=false;
- setTextInput();
- }else
- if(_can_select)
- {
- if(ButtonPd(touch_state))
- {
- if(_edit.cur!=pos || _edit.sel>=0)
- {
- _edit.cur=pos;
- _edit.sel=-1;
- setTextInput();
- }
- }else
- if(pos!=_edit.cur)
- {
- if(_edit.sel<0)_edit.sel=_edit.cur;
- _edit.cur=pos;
- setTextInput();
- }
- // scroll offset
- Flt x=ts.textWidth(displayText(), _edit.cur) + _offset + ts.size.x*TEXTLINE_OFFSET, w=clientWidth(), margin=ts.size.x*TEXTLINE_MARGIN;
- if( x< margin)_offset =Min(Time.d()*2+_offset, 0.0f);else
- if( x>=w-margin)_offset-=Min(Time.d()*2 , x-w*0.5f);
- }
- }
- }else _can_select=true;
- }
- }
- void TextLine::draw(C GuiPC &gpc)
- {
- if(visible() && gpc.visible)
- if(GuiSkin *skin=getSkin())
- {
- Bool enabled=(T.enabled() && gpc.enabled);
- Rect rect=T.rect()+gpc.offset, ext_rect;
- C PanelImage *panel_image ; // may be null
- C Color *panel_color, *text_color; // never null
- if(enabled){panel_image=skin->textline.normal (); panel_color=&skin->textline. normal_panel_color; text_color=&skin->textline. normal_text_color;}
- else {panel_image=skin->textline.disabled(); panel_color=&skin->textline.disabled_panel_color; text_color=&skin->textline.disabled_text_color;}
- if(panel_image)panel_image->extendedRect(rect, ext_rect);else ext_rect=rect;
- if(Cuts(ext_rect, gpc.clip))
- {
- D.clip(gpc.clip);
- if(panel_image )panel_image->draw(*panel_color, TRANSPARENT, rect);else
- if(panel_color->a )rect .draw(*panel_color , true );
- if(skin->textline.rect_color.a)rect .draw(skin->textline.rect_color, false);
- // draw text
- if(TextStyle *text_style=skin->textline.text_style())
- {
- Bool active=(Gui.kb()==this && enabled && ((Gui._overlay_textline==this) ? Equal(Gui._overlay_textline_offset, gpc.offset) : true)); // if this is the overlay textline, then draw cursor and editing only if it matches the overlay offset
- if(T().is() || active || show_find || hint.is())
- {
- Rect clip_rect=rect; clip_rect.max.x=clip_rect.min.x+clientWidth(); D.clip(clip_rect&gpc.clip);
- TextStyleParams ts=*text_style; ts.size=rect.h()*skin->textline.text_size; ts.align.set(1, 0); ts.color=ColorMul(ts.color, *text_color);
- #if DEFAULT_FONT_FROM_CUSTOM_SKIN
- if(!ts.font())ts.font(skin->font()); // adjust font in case it's empty and the custom skin has a different font than the 'Gui.skin'
- #endif
- Flt x=rect.min.x + ts.size.x*TEXTLINE_OFFSET, y=rect.centerY();
- if(T().is() || active)
- {
- if(active)ts.edit=&_edit;
- D.text(ts, x+_offset, y, displayText());
- }else
- {
- if(show_find)if(Image *image=skin->textline.find_image())
- {
- x=rect.min.x+image->aspect()*rect.h(); // set new x as the right side of image, so that potential 'hint' will be drawn to the right
- image->draw(Rect(rect.min.x, rect.min.y, x, rect.max.y)); // draw on the left
- }
- if(hint.is())
- {
- ts.color.a=((ts.color.a*96)>>8); ts.size*=0.85f; D.text(ts, x, y, hint);
- }
- }
- }
- }
- if(showClear())reset.draw(gpc);
- if(kb_lit && Gui.kb()==this){D.clip(gpc.clip); Gui.kbLit(this, rect, skin);}
- }
- }
- }
- /******************************************************************************/
- // IO
- /******************************************************************************/
- Bool TextLine::save(File &f, CChar *path)C
- {
- if(super::save(f, path))
- {
- f.cmpUIntV(7); // version
- f<<kb_lit<<show_find<<_edit.password<<_max_length<<hint<<_text;
- f.putBool(reset.visible());
- f._putAsset(_skin.name(path));
- return f.ok();
- }
- return false;
- }
- Bool TextLine::load(File &f, CChar *path)
- {
- del();
- createReset();
- if(super::load(f, path))switch(f.decUIntV()) // version
- {
- case 7:
- {
- _type=GO_TEXTLINE;
- f>>kb_lit>>show_find>>_edit.password>>_max_length>>hint>>_text;
- reset.visible(f.getBool());
- _skin.require(f._getAsset(), path);
- if(f.ok())return true;
- }break;
- case 6:
- {
- _type=GO_TEXTLINE;
- Bool kb_catch; f>>kb_catch>>kb_lit>>show_find>>_edit.password>>_max_length; f._getStr2(hint)._getStr2(_text);
- reset.visible(f.getBool());
- _skin.require(f._getAsset(), path);
- if(f.ok())return true;
- }break;
- case 5:
- {
- _type=GO_TEXTLINE;
- f>>kb_lit; f.skip(8); f>>_edit.password>>_max_length; f._getStr2(_text); f._getStr2();
- if(f.ok())return true;
- }break;
- case 4:
- {
- _type=GO_TEXTLINE;
- f>>kb_lit; f.skip(8); f>>_edit.password>>_max_length; f._getStr(_text);
- if(f.ok())return true;
- }break;
- case 3:
- {
- _type=GO_TEXTLINE;
- f>>kb_lit; f.skip(8); f>>_edit.password>>_max_length; f._getStr(_text);
- if(f.ok())return true;
- }break;
- case 2:
- {
- _type=GO_TEXTLINE;
- f>>kb_lit; f.skip(8); f>>_edit.password; f._getStr(_text);
- if(f.ok())return true;
- }break;
- case 1:
- {
- _type=GO_TEXTLINE;
- f>>kb_lit; f.skip(8); f>>_offset; _text=f._getStr16();
- if(f.ok())return true;
- }break;
- case 0:
- {
- _type=GO_TEXTLINE;
- f>>kb_lit; f.skip(8); f>>_offset; _text=f._getStr8();
- if(f.ok())return true;
- }break;
- }
- del(); return false;
- }
- /******************************************************************************/
- }
- /******************************************************************************/
|