diff --git a/legacy/project/Build.xml b/legacy/project/Build.xml index a5a5a307a..92430f854 100644 --- a/legacy/project/Build.xml +++ b/legacy/project/Build.xml @@ -208,6 +208,8 @@ + + @@ -243,6 +245,10 @@ + + @@ -416,6 +422,7 @@ + diff --git a/legacy/project/include/Display.h b/legacy/project/include/Display.h index c2ecd76ff..1759e99f8 100644 --- a/legacy/project/include/Display.h +++ b/legacy/project/include/Display.h @@ -192,6 +192,7 @@ public: Graphics &GetGraphics(); virtual Matrix GetFullMatrix(bool inWithStageScaling); Matrix &GetLocalMatrix(); + virtual void modifyLocalMatrix(Matrix &ioMatrix) { } ColorTransform &GetLocalColorTransform() { return colorTransform; } ColorTransform GetFullColorTransform(); const FilterList &getFilters() { return filters; } diff --git a/legacy/project/include/Font.h b/legacy/project/include/Font.h index 10cc711c5..0acf1befb 100644 --- a/legacy/project/include/Font.h +++ b/legacy/project/include/Font.h @@ -18,7 +18,18 @@ struct TextLineMetrics float ascent; float descent; float height; + + void fontToLocal(double inScale) + { + ascent *= inScale; + descent *= inScale; + height = ascent + descent; + } + + // From text format - local coords float leading; + + // The horizontal components are in local coords float width; float x; }; @@ -106,9 +117,9 @@ struct CharGroup CharGroup() : mChar0(0), mFontHeight(0), mFormat(0), mFont(0) { }; ~CharGroup(); void Clear(); - bool UpdateFont(double inScale,GlyphRotation inRotation,bool inNative); + bool UpdateFont(double inScale,bool inNative); void UpdateMetrics(TextLineMetrics &ioMetrics); - int Height(); + double Height(double inFontToLocal); int Chars() { return mString.size(); } void ApplyFormat(TextFormat *inFormat); @@ -129,11 +140,11 @@ struct Line void Clear() { memset(this,0,sizeof(*this)); } TextLineMetrics mMetrics; - int mY0; - int mChar0; - int mChars; - int mCharGroup0; - int mCharInGroup0; + float mY0; + int mChar0; + int mChars; + int mCharGroup0; + int mCharInGroup0; }; typedef QuickVec Lines; @@ -173,7 +184,7 @@ class Font : public Object }; public: - static Font *Create(TextFormat &inFormat,double inScale,GlyphRotation inRot, bool inNative,bool inInitRef=true); + static Font *Create(TextFormat &inFormat,double inScale, bool inNative,bool inInitRef=true); Font *IncRef() { Object::IncRef(); return this; } @@ -183,11 +194,9 @@ public: bool IsNative() { return mFace && mFace->IsNative(); } - GlyphRotation Rotation() { return mRotation; } - int Height(); private: - Font(FontFace *inFace, int inPixelHeight, GlyphRotation inRotation,bool inInitRef); + Font(FontFace *inFace, int inPixelHeight, bool inInitRef); ~Font(); @@ -198,7 +207,6 @@ private: int mPixelHeight; int mCurrentSheet; - GlyphRotation mRotation; }; class FontCache diff --git a/legacy/project/include/Graphics.h b/legacy/project/include/Graphics.h index 3414e9332..b0e7571c3 100644 --- a/legacy/project/include/Graphics.h +++ b/legacy/project/include/Graphics.h @@ -214,7 +214,7 @@ public: GraphicsStroke *IncRef() { Object::IncRef(); return this; } GraphicsStroke *AsStroke() { return this; } - int Version() const { return fill->Version(); } + int Version() const { return fill ? fill->Version() : 0; } bool IsClear() { return false; } diff --git a/legacy/project/include/TextField.h b/legacy/project/include/TextField.h index 2077ad86a..147fa3dbd 100644 --- a/legacy/project/include/TextField.h +++ b/legacy/project/include/TextField.h @@ -12,8 +12,6 @@ class TiXmlNode; namespace nme { - - class TextField : public DisplayObject { public: @@ -38,10 +36,13 @@ public: void setSelectable(bool inSelectable) { selectable = inSelectable; } void setTextColor(int inColor); int getTextColor() { return textColor; } + bool isLineVisible(int inLine) const; bool getIsInput() { return isInput; } void setIsInput(bool inIsInput); AutoSizeMode getAutoSize() { return autoSize; } void setAutoSize(int inAutoSize); + void modifyLocalMatrix(Matrix &ioMatrix); + int getCaretIndex() { return caretIndex; } int getMaxScrollH() { Layout(); return maxScrollH; } @@ -51,6 +52,7 @@ public: void setScrollH(int inScrollH); int getScrollV() { return scrollV; } void setScrollV(int inScrollV); + void setScrollVClearSel(int inScrollV,bool inClearSel); int getNumLines() { Layout(); return mLines.size(); } int getSelectionBeginIndex(); int getSelectionEndIndex(); @@ -80,6 +82,8 @@ public: int getLineOffset(int inLine); WString getLineText(int inLine); + void toScreenGrid(UserPoint &ioPoint,const Matrix &inMatrix); + void highlightRect(double x0, double y1, double w, double h); TextLineMetrics *getLineMetrics(int inLine); double getWidth(); @@ -92,7 +96,7 @@ public: WString getText(); void setText(const WString &inString); - int getLength(); + int getLength() const; double getTextHeight(); double getTextWidth(); @@ -130,19 +134,17 @@ public: void Render( const RenderTarget &inTarget, const RenderState &inState ); - // Display-object like properties - // Glyphs are laid out in a local pixel coordinate space, which is related to the - // render-target window co-ordinates by the folling members - double mLayoutScaleH; - double mLayoutScaleV; - GlyphRotation mLayoutRotation; - // Unscaled size, as specified by application - double boundsWidth; - double boundsHeight; - // Local pixel space - int textWidth; - int textHeight; - Rect mActiveRect; + bool screenGrid; + double fontScale; + double fontToLocal; + + // Local coordinates + double explicitWidth; + double fieldWidth; + double fieldHeight; + double textWidth; + double textHeight; + DRect mActiveRect; void GetExtent(const Transform &inTrans, Extent2DF &outExt,bool inForBitmap,bool inIncludeStroke); Cursor GetCursor(); @@ -153,6 +155,7 @@ public: void OnKey(Event &inEvent); void OnScrollWheel(int inDirection); void DeleteSelection(); + void ClearSelection(); void DeleteChars(int inFirst,int inEnd); void InsertString(WString &ioString); void ShowCaret(bool inFromDrag=false); @@ -185,30 +188,32 @@ private: void SplitGroup(int inGroup,int inPos); void BuildBackground(); - UserPoint TargetToRect(const Matrix &inMat,const UserPoint &inPoint); - UserPoint RectToTarget(const Matrix &inMat,const UserPoint &inPoint); - int PointToChar(int inX,int inY); + int PointToChar(UserPoint inPoint) const; int LineFromChar(int inChar); int GroupFromChar(int inChar); - int EndOfCharX(int inChar,int inLine); - int EndOfLineX(int inLine); - ImagePoint GetScrollPos(); - ImagePoint GetCursorPos(); + double EndOfCharX(int inChar,int inLine) const; + double EndOfLineX(int inLine) const; + UserPoint GetScrollPos() const; + UserPoint GetCursorPos() const; void OnChange(); bool mLinesDirty; bool mGfxDirty; bool mFontsDirty; + bool mTilesDirty; + bool mCaretDirty; bool mHasCaret; + double mBlink0; CharGroups mCharGroups; Lines mLines; - QuickVec mCharPos; + QuickVec mCharPos; Graphics *mCaretGfx; Graphics *mHighlightGfx; + Graphics *mTiles; int mLastCaretHeight; int mLastUpDownX; diff --git a/legacy/project/include/Tilesheet.h b/legacy/project/include/Tilesheet.h index f4bfb5365..72618121a 100644 --- a/legacy/project/include/Tilesheet.h +++ b/legacy/project/include/Tilesheet.h @@ -23,7 +23,7 @@ public: Tilesheet *IncRef() { Object::IncRef(); return this; } - int AllocRect(int inW,int inH,float inOx = 0, float inOy = 0); + int AllocRect(int inW,int inH,float inOx = 0, float inOy = 0,bool inAlphaBorder=false); int addTileRect(const Rect &inRect,float inOx=0, float inOy=0); const Tile &GetTile(int inID) { return mTiles[inID]; } Surface &GetSurface() { return *mSheet; } diff --git a/legacy/project/include/nme/NmeCffi.h b/legacy/project/include/nme/NmeCffi.h index d4c26ce2f..8def02e09 100644 --- a/legacy/project/include/nme/NmeCffi.h +++ b/legacy/project/include/nme/NmeCffi.h @@ -11,24 +11,26 @@ namespace nme extern vkind gObjectKind; + +namespace +{ + inline void release_object(value inValue) + { + if (val_is_kind(inValue,gObjectKind)) + { + Object *obj = (Object *)val_to_kind(inValue,gObjectKind); + if (obj) + obj->DecRef(); + } + } +} + + inline value ObjectToAbstract(Object *inObject) { - struct releaser - { - static void release_object(value inValue) - { - if (val_is_kind(inValue,gObjectKind)) - { - Object *obj = (Object *)val_to_kind(inValue,gObjectKind); - if (obj) - obj->DecRef(); - } - } - }; - inObject->IncRef(); value result = alloc_abstract(gObjectKind,inObject); - val_gc(result,releaser::release_object); + val_gc(result,release_object); return result; } diff --git a/legacy/project/include/nme/Pixel.h b/legacy/project/include/nme/Pixel.h index e0df01815..27d38a528 100644 --- a/legacy/project/include/nme/Pixel.h +++ b/legacy/project/include/nme/Pixel.h @@ -39,6 +39,11 @@ struct ARGB a = alpha<0 ? 0 : alpha >255 ? 255 : alpha; } + inline float getRedFloat() { return r/255.0; } + inline float getGreenFloat() { return g/255.0; } + inline float getBlueFloat() { return b/255.0; } + inline float getAlphaFloat() { return a/255.0; } + inline int ToInt() const { return ival; } inline void Set(int inVal) { ival = inVal; } inline void SetRGB(int inVal) { ival = inVal | 0xff000000; } diff --git a/legacy/project/src/ExternalInterface.cpp b/legacy/project/src/ExternalInterface.cpp index 7fddc551c..d9d754dd0 100644 --- a/legacy/project/src/ExternalInterface.cpp +++ b/legacy/project/src/ExternalInterface.cpp @@ -397,6 +397,7 @@ namespace nme { DEFINE_LIME_PRIM_1(font_set_factory); DEFINE_LIME_PRIM_2(font_register_font); DEFINE_LIME_PRIM_0(gl_get_error); + DEFINE_LIME_PRIM_1(gl_get_extension); DEFINE_LIME_PRIM_0(gl_finish); DEFINE_LIME_PRIM_0(gl_flush); DEFINE_LIME_PRIM_0(gl_version); diff --git a/legacy/project/src/common/CURL.cpp b/legacy/project/src/common/CURL.cpp index fd18315f8..6d2eab489 100644 --- a/legacy/project/src/common/CURL.cpp +++ b/legacy/project/src/common/CURL.cpp @@ -405,7 +405,7 @@ extern get_file_callback_func get_file_callback; } #if (defined(HX_MACOS) || defined(ANDROID) ) && defined(NME_CURL_SSL) -//#define TRY_GET_FILE +#define TRY_GET_FILE #endif #ifdef TRY_GET_FILE diff --git a/legacy/project/src/common/Display.cpp b/legacy/project/src/common/Display.cpp index 9046e44fd..a3b55a4f3 100644 --- a/legacy/project/src/common/Display.cpp +++ b/legacy/project/src/common/Display.cpp @@ -2,7 +2,6 @@ #include #include -#include "TextField.h" #ifndef M_PI #define M_PI 3.1415926535897932385 @@ -12,11 +11,6 @@ #include #endif -#if defined(NME_S3D) && defined(ANDROID) -#include -#include -#endif - namespace nme { @@ -300,7 +294,8 @@ void DisplayObject::DirtyCache(bool inParentOnly) Matrix DisplayObject::GetFullMatrix(bool inStageScaling) { if (mParent) - return mParent->GetFullMatrix(inStageScaling).Mult(GetLocalMatrix().Translated(-scrollRect.x,-scrollRect.y)); + return mParent->GetFullMatrix(inStageScaling).Mult(GetLocalMatrix(). + Translated(-scrollRect.x,-scrollRect.y)); return GetLocalMatrix().Translated(-scrollRect.x,-scrollRect.y); } @@ -350,6 +345,7 @@ Matrix &DisplayObject::GetLocalMatrix() #ifdef NME_S3D mLocalMatrix.mtz = z; #endif + modifyLocalMatrix(mLocalMatrix); } return mLocalMatrix; } @@ -1528,764 +1524,7 @@ void DisplayObjectContainer::ClearCacheDirty() } -// --- BitmapCache --------------------------------------------------------- -static int sBitmapVersion = 1; - -BitmapCache::BitmapCache(Surface *inSurface,const Transform &inTrans, - const Rect &inRect,bool inMaskOnly, BitmapCache *inMask) -{ - mBitmap = inSurface->IncRef(); - mMatrix = *inTrans.mMatrix; - mScale9 = *inTrans.mScale9; - mRect = inRect; - mVersion = sBitmapVersion++; - if (!mVersion) - mVersion = sBitmapVersion++; - mMaskVersion = inMask ? inMask->mVersion : 0; - mMaskOffset = inMask ? ImagePoint(inMask->mTX,inMask->mTY) : ImagePoint(0,0); - mTX = mTY = 0; -} - -BitmapCache::~BitmapCache() -{ - mBitmap->DecRef(); -} - - -bool BitmapCache::StillGood(const Transform &inTransform, const Rect &inVisiblePixels, BitmapCache *inMask) -{ - if (!mMatrix.IsIntTranslation(*inTransform.mMatrix,mTX,mTY) || mScale9!=*inTransform.mScale9) - return false; - - if (inMask) - { - if (inMask->mVersion!=mMaskVersion) - return false; - if (mMaskOffset != ImagePoint(inMask->mTX, inMask->mTY) ) - return false; - } - else if (mMaskVersion) - return false; - - // Translate our cached pixels to this new position ... - Rect translated = mRect.Translated(mTX,mTY); - if (translated.Contains(inVisiblePixels)) - return true; - - return false; -} - - -void BitmapCache::Render(const RenderTarget &inTarget,const Rect &inClipRect, const BitmapCache *inMask,BlendMode inBlend) -{ - if (mBitmap) - { - int tint = 0xffffffff; - if (inTarget.mPixelFormat!=pfAlpha && mBitmap->Format()==pfAlpha) - tint = 0xff000000; - - Rect src( mRect.x+mTX, mRect.y+mTY, mRect.w, mRect.h); - int ox = src.x; - int oy = src.y; - src = src.Intersect(inClipRect); - if (!src.HasPixels()) - return; - ox -= src.x; - oy -= src.y; - src.Translate(-mRect.x - mTX,-mRect.y-mTY); - - - if (inTarget.IsHardware()) - { - //__android_log_print(ANDROID_LOG_INFO,"BitmapCache", "Render %dx%d + (%d,%d) -> %dx%d + (%d,%d)", - //mRect.w,mRect.h, mRect.x + mTX , mRect.y+mTY, - //inTarget.mRect.w, inTarget.mRect.h, inTarget.mRect.x, inTarget.mRect.y ); - inTarget.mHardware->SetViewport(inTarget.mRect); - inTarget.mHardware->BeginBitmapRender(mBitmap,tint); - inTarget.mHardware->RenderBitmap(src, mRect.x+mTX-ox, mRect.y+mTY-oy); - inTarget.mHardware->EndBitmapRender(); - } - else - { - // TX,TX is set in StillGood function - mBitmap->BlitTo(inTarget, src, mRect.x+mTX-ox, mRect.y+mTY-oy,inBlend,inMask,tint); - } - } -} - -void BitmapCache::PushTargetOffset(const ImagePoint &inOffset, ImagePoint &outBuffer) -{ - outBuffer = ImagePoint(mTX,mTY); - mTX -= inOffset.x; - mTY -= inOffset.y; -} - -void BitmapCache::PopTargetOffset(ImagePoint &inBuffer) -{ - mTX = inBuffer.x; - mTY = inBuffer.y; -} - - -bool BitmapCache::HitTest(double inX, double inY) -{ - double x0 = mRect.x+mTX; - double y0 = mRect.y+mTY; - //printf("BMP hit %f,%f %f,%f ... %d,%d\n", inX, inY, x0,y0, mRect.w, mRect.h ); - return x0<=inX && y0<=inY && (inX<=x0+mRect.w) && (inY<=y0+mRect.h); -} - - - -// --- Stage --------------------------------------------------------------- - - -// Helper class.... -class AutoStageRender -{ - Surface *mSurface; - Stage *mToFlip; - RenderTarget mTarget; -public: - AutoStageRender(Stage *inStage,int inRGB) - { - mSurface = inStage->GetPrimarySurface(); - mToFlip = inStage; - mTarget = mSurface->BeginRender( Rect(mSurface->Width(),mSurface->Height()),false ); - - mSurface->Clear( (inRGB | 0xff000000) & inStage->getBackgroundMask() ); - } - int Width() const { return mSurface->Width(); } - int Height() const { return mSurface->Height(); } - ~AutoStageRender() - { - mSurface->EndRender(); - mToFlip->Flip(); - } - const RenderTarget &Target() { return mTarget; } -}; - -Stage *Stage::gCurrentStage = 0; - -Stage::Stage(bool inInitRef) : DisplayObjectContainer(inInitRef) -{ - gCurrentStage = this; - mHandler = 0; - mHandlerData = 0; - opaqueBackground = 0xffffffff; - mFocusObject = 0; - mMouseDownObject = 0; - mSimpleButton = 0; - focusRect = true; - mLastMousePos = UserPoint(0,0); - scaleMode = ssmShowAll; - mNominalWidth = 100; - mNominalHeight = 100; - mNextWake = 0.0; - displayState = sdsNormal; - align = saTopLeft; - #ifdef NME_S3D - autos3d = true; - #endif - - #if defined(IPHONE) || defined(ANDROID) || defined(WEBOS) || defined(TIZEN) - quality = sqLow; - #else - quality = sqBest; - #endif -} - -Stage::~Stage() -{ - if (gCurrentStage==this) - gCurrentStage = 0; - if (mFocusObject) - mFocusObject->DecRef(); - if (mMouseDownObject) - mMouseDownObject->DecRef(); -} - -void Stage::SetNextWakeDelay(double inNextWake) -{ - mNextWake = inNextWake + GetTimeStamp(); -} - -void Stage::SetFocusObject(DisplayObject *inObj,FocusSource inSource,int inKey) -{ - if (inObj==mFocusObject) - return; - - if (mHandler) - { - Event focus(etFocus); - focus.id = inObj ? inObj->id : 0; - focus.value = inSource; - focus.code = inKey; - - mHandler(focus,mHandlerData); - - if (inSource!=fsProgram && focus.result==erCancel) - return; - } - - - if (!inObj || inObj->getStage()!=this) - { - if (mFocusObject) - { - mFocusObject->Unfocus(); - mFocusObject->DecRef(); - } - mFocusObject = 0; - } - else - { - inObj->IncRef(); - if (mFocusObject) - { - mFocusObject->Unfocus(); - mFocusObject->DecRef(); - } - mFocusObject = inObj; - inObj->Focus(); - } - -} - -void Stage::SetNominalSize(int inWidth, int inHeight) -{ - mNominalWidth = inWidth; - mNominalHeight = inHeight; - CalcStageScaling( getStageWidth(), getStageHeight() ); -} - - -void Stage::SetEventHandler(EventHandler inHander,void *inUserData) -{ - mHandler = inHander; - mHandlerData = inUserData; -} - -void Stage::HandleEvent(Event &inEvent) -{ - gCurrentStage = this; - DisplayObject *hit_obj = 0; - - bool primary = inEvent.flags & efPrimaryTouch; - - if ( (inEvent.type==etMouseMove || inEvent.type==etMouseDown || - inEvent.type==etTouchBegin || inEvent.type==etTouchMove ) - && primary ) - mLastMousePos = UserPoint(inEvent.x, inEvent.y); - - if (mMouseDownObject && primary) - { - switch(inEvent.type) - { - case etTouchMove: - case etMouseMove: - if (inEvent.flags & efLeftDown) - { - mMouseDownObject->Drag(inEvent); - break; - } - // fallthrough - case etMouseClick: - case etMouseDown: - case etMouseUp: - case etTouchBegin: - case etTouchTap: - case etTouchEnd: - mMouseDownObject->EndDrag(inEvent); - mMouseDownObject->DecRef(); - mMouseDownObject = 0; - break; - default: break; - } - } - - if (inEvent.type==etKeyDown || inEvent.type==etKeyUp) - { - inEvent.id = mFocusObject ? mFocusObject->id : id; - if (mHandler) - mHandler(inEvent,mHandlerData); - if (inEvent.result==0 && mFocusObject) - mFocusObject->OnKey(inEvent); - #ifdef ANDROID - // Non-cancelled back key ... - if (inEvent.result==0 && inEvent.value==27 && inEvent.type == etKeyUp) - { - StopAnimation(); - } - #endif - return; - } - - if (inEvent.type==etResize) - { - CalcStageScaling( inEvent.x, inEvent.y); - } - - if (inEvent.type==etMouseMove || inEvent.type==etMouseDown || - inEvent.type==etMouseUp || inEvent.type==etMouseClick || - inEvent.type==etTouchBegin || inEvent.type==etTouchEnd || - inEvent.type==etTouchMove || inEvent.type==etTouchTap - ) - { - UserPoint pixels(inEvent.x,inEvent.y); - hit_obj = HitTest(pixels); - //if (inEvent.type!=etTouchMove) - //ELOG(" type=%d %d,%d obj=%p (%S)", inEvent.type, inEvent.x, inEvent.y, hit_obj, hit_obj?hit_obj->name.c_str():L"(none)"); - - SimpleButton *but = hit_obj ? dynamic_cast(hit_obj) : 0; - inEvent.id = hit_obj ? hit_obj->id : id; - Cursor cur = hit_obj ? hit_obj->GetCursor() : curPointer; - - if (mSimpleButton && (inEvent.flags & efLeftDown) ) - { - // Don't change simple button if dragging ... - } - else if (but!=mSimpleButton) - { - if (but) - but->IncRef(); - if (mSimpleButton) - { - SimpleButton *s = mSimpleButton; - mSimpleButton = 0; - s->setMouseState(SimpleButton::stateUp); - s->DecRef(); - } - mSimpleButton = but; - } - - if (mSimpleButton) - { - bool over = but==mSimpleButton; - bool down = (inEvent.flags & efLeftDown); - mSimpleButton->setMouseState( over ? ( down ? - SimpleButton::stateDown : SimpleButton::stateOver) : SimpleButton::stateUp ); - if (!down && !over) - { - SimpleButton *s = mSimpleButton; - mSimpleButton = 0; - s->DecRef(); - } - else if (mSimpleButton->getUseHandCursor()) - cur = curHand; - } - - SetCursor( (gMouseShowCursor || cur>=curTextSelect0) ? cur : curNone ); - - UserPoint stage = mStageScale.ApplyInverse(pixels); - inEvent.x = stage.x; - inEvent.y = stage.y; - } - - - if (hit_obj) - hit_obj->IncRef(); - - if (mHandler) - mHandler(inEvent,mHandlerData); - - if (hit_obj) - { - if ( (inEvent.type==etMouseDown || - (inEvent.type==etTouchBegin && (inEvent.flags & efPrimaryTouch) )) - && inEvent.result!=erCancel ) - { - if (hit_obj->WantsFocus()) - SetFocusObject(hit_obj,fsMouse); - #if defined(IPHONE) || defined(ANDROID) || defined(WEBOS) || defined(TIZEN) - else - { - EnablePopupKeyboard(false); - SetFocusObject(0,fsMouse); - } - #endif - } - - if (inEvent.type==etMouseDown || (inEvent.type==etTouchBegin && primary) ) - { - if (hit_obj->CaptureDown(inEvent)) - { - hit_obj->IncRef(); - mMouseDownObject = hit_obj; - } - } - if (inEvent.type==etMouseUp && (inEvent.value==3 || inEvent.value==4) ) - { - TextField *text = dynamic_cast(hit_obj); - if (text && text->mouseWheelEnabled) - text->OnScrollWheel(inEvent.value==3 ? -1 : 1); - } - } - #if defined(IPHONE) || defined(ANDROID) || defined(WEBOS) || defined(TIZEN) - else if (inEvent.type==etMouseClick || inEvent.type==etMouseDown || - (inEvent.type==etTouchBegin && (inEvent.flags & efPrimaryTouch) )) - { - EnablePopupKeyboard(false); - SetFocusObject(0); - } - #endif - - - if (hit_obj) - hit_obj->DecRef(); -} - -void Stage::setOpaqueBackground(uint32 inBG) -{ - opaqueBackground = inBG | 0xff000000; - DirtyCache(); -} - - -void Stage::RemovingFromStage(DisplayObject *inObject) -{ - DisplayObject *b = mSimpleButton; - while(b) - { - if (b==inObject) - { - mSimpleButton->DecRef(); - mSimpleButton = 0; - break; - } - b = b->getParent(); - } - - - DisplayObject *f = mFocusObject; - while(f) - { - if (f==inObject) - { - mFocusObject->DecRef(); - mFocusObject = 0; - break; - } - f = f->getParent(); - } - - DisplayObject *m = mMouseDownObject; - while(m) - { - if (m==inObject) - { - mMouseDownObject->DecRef(); - mMouseDownObject = 0; - break; - } - m = m->getParent(); - } - -} - - -void Stage::CalcStageScaling(double inNewWidth,double inNewHeight) -{ - double StageScaleX=1; - double StageScaleY=1; - double StageOX=0; - double StageOY=0; - - if (inNewWidth<=0 || inNewHeight<=0) - return; - - if (scaleMode!=ssmNoScale) - { - StageScaleX = inNewWidth/(double)mNominalWidth; - StageScaleY = inNewHeight/(double)mNominalHeight; - - if (scaleMode==ssmNoBorder) - { - if (StageScaleX>StageScaleY) - StageScaleY = StageScaleX; - else - StageScaleX = StageScaleY; - } - else if (scaleMode==ssmShowAll) - { - if (StageScaleXFinishEditOnEnter(); - return false; -} - -int Stage::GetAA() -{ - switch(quality) - { - case sqLow: return 1; - case sqMedium: return 2; - case sqHigh: - case sqBest: - return 4; - } - return 1; -} - - -#ifdef HX_LIME // { - -void Stage::RenderStage() -{ - ColorTransform::TidyCache(); - AutoStageRender render(this,opaqueBackground); - - #if !defined(NME_S3D) || !defined(ANDROID) - - if (render.Target().IsHardware()) - render.Target().mHardware->SetQuality(quality); - - RenderState state(0, GetAA() ); - - state.mTransform.mMatrix = &mStageScale; - - state.mClipRect = Rect( render.Width(), render.Height() ); - - state.mPhase = rpBitmap; - state.mRoundSizeToPOW2 = render.Target().IsHardware(); - Render(render.Target(),state); - - state.mPhase = rpRender; - Render(render.Target(),state); - - #else - - S3DEye start = EYE_MIDDLE; - S3DEye end = EYE_MIDDLE; - if(autos3d && S3D::GetEnabled()) { - - start = EYE_LEFT; - end = EYE_RIGHT; - - } - - for(int eye = start; eye <= end; eye++) { - - if (render.Target().IsHardware()) - { - render.Target().mHardware->SetS3DEye(eye); - render.Target().mHardware->SetQuality(quality); - } - - RenderState state(0, GetAA() ); - - state.mTransform.mMatrix = &mStageScale; - - state.mClipRect = Rect( render.Width(), render.Height() ); - - state.mPhase = rpBitmap; - state.mRoundSizeToPOW2 = render.Target().IsHardware(); - Render(render.Target(),state); - - state.mPhase = rpRender; - Render(render.Target(),state); - - } - - if(autos3d && S3D::GetEnabled()) { - render.Target().mHardware->EndS3DRender(); - } - - #endif - - // Clear alpha masks -} -void Stage::BeginRenderStage(bool) { } -void Stage::EndRenderStage() { } - -#else // } nme(not lime) split render stage into 3 phases .. { - -void Stage::BeginRenderStage(bool inClear) -{ - Surface *surface = GetPrimarySurface(); - currentTarget = surface->BeginRender( Rect(surface->Width(),surface->Height()),false ); - if (inClear) - surface->Clear( (opaqueBackground | 0xff000000) & getBackgroundMask() ); -} - -void Stage::RenderStage() -{ - ColorTransform::TidyCache(); - - if (currentTarget.IsHardware()) - currentTarget.mHardware->SetQuality(quality); - - RenderState state(0, GetAA() ); - - state.mTransform.mMatrix = &mStageScale; - - state.mClipRect = Rect( currentTarget.Width(), currentTarget.Height() ); - - state.mPhase = rpBitmap; - state.mRoundSizeToPOW2 = currentTarget.IsHardware(); - Render(currentTarget,state); - - state.mPhase = rpRender; - Render(currentTarget,state); -} - -void Stage::EndRenderStage() -{ - currentTarget = RenderTarget(); - GetPrimarySurface()->EndRender(); - ClearCacheDirty(); - Flip(); -} - -#endif // } - -bool Stage::BuildCache() -{ - Surface *surface = GetPrimarySurface(); - RenderState state(surface, GetAA() ); - state.mTransform.mMatrix = &mStageScale; - bool wasDirty = false; - state.mWasDirtyPtr = &wasDirty; - - state.mPhase = rpBitmap; - - RenderTarget target(state.mClipRect, surface->GetHardwareRenderer()); - state.mRoundSizeToPOW2 = surface->GetHardwareRenderer(); - Render(target,state); - - return wasDirty; -} - -double Stage::getStageWidth() -{ - Surface *s = GetPrimarySurface(); - if (!s) return 0; - return s->Width(); -} - -double Stage::getStageHeight() -{ - Surface *s = GetPrimarySurface(); - if (!s) return 0; - return s->Height(); -} - - -void Stage::setScaleMode(int inMode) -{ - scaleMode = (StageScaleMode)inMode; - CalcStageScaling( getStageWidth(), getStageHeight() ); -} - -void Stage::setAlign(int inAlign) -{ - align = (StageAlign)inAlign; - CalcStageScaling( getStageWidth(), getStageHeight() ); -} - -void Stage::setQuality(int inQuality) -{ - quality = (StageQuality)inQuality; - DirtyCache(); -} - -void Stage::setDisplayState(int inDisplayState) -{ - displayState = (StageDisplayState)inDisplayState; - SetFullscreen(inDisplayState>0); -} - - -Matrix Stage::GetFullMatrix(bool inStageScaling) -{ - if (!inStageScaling) - return DisplayObject::GetFullMatrix(false); - - return mStageScale.Mult(GetLocalMatrix()); -} - - - -DisplayObject *Stage::HitTest(UserPoint inStage,DisplayObject *inRoot,bool inRecurse) -{ - Surface *surface = GetPrimarySurface(); - - RenderTarget target = surface->BeginRender( Rect(surface->Width(),surface->Height()),true ); - - RenderState state(0, GetAA() ); - state.mClipRect = Rect( inStage.x, inStage.y, 1, 1 ); - Matrix m = mStageScale; - if (inRoot) - m = inRoot->GetFullMatrix(true); - state.mTransform.mMatrix = &m; - - - state.mRoundSizeToPOW2 = target.IsHardware(); - state.mPhase = rpHitTest; - state.mRecurse = inRecurse; - - (inRoot ? inRoot : this) -> Render(target,state); - - surface->EndRender(); - - // ELOG("Stage hit %f,%f -> %p\n", inStage.x, inStage.y, state.mHitResult ); - - return state.mHitResult; -} } // end namespace nme diff --git a/legacy/project/src/common/ExternalInterface.cpp b/legacy/project/src/common/ExternalInterface.cpp index 90438cff0..6acc72ab2 100644 --- a/legacy/project/src/common/ExternalInterface.cpp +++ b/legacy/project/src/common/ExternalInterface.cpp @@ -576,7 +576,8 @@ void ToValue(value &outVal,const ColorTransform &inTrans) void FromValue(value obj, URLRequest &request) { request.url = val_string( val_field(obj, _id_url) ); - request.userAgent = val_string( val_field(obj, _id_userAgent) ); + value userAgent = val_field(obj, _id_userAgent); + request.userAgent = val_is_string(userAgent) ? val_string( userAgent ) : ""; request.authType = val_field_numeric(obj, _id_authType ); request.credentials = val_string( val_field(obj, _id_credentials) ); request.cookies = val_string( val_field(obj, _id_cookieString) ); diff --git a/legacy/project/src/common/Font.cpp b/legacy/project/src/common/Font.cpp index 34fc9c8b0..1a04cd74b 100644 --- a/legacy/project/src/common/Font.cpp +++ b/legacy/project/src/common/Font.cpp @@ -140,10 +140,9 @@ FontFace *FontFace::CreateCFFIFont(const TextFormat &inFormat,double inScale) // --- Font ---------------------------------------------------------------- -Font::Font(FontFace *inFace, int inPixelHeight, GlyphRotation inRotation,bool inInitRef) : +Font::Font(FontFace *inFace, int inPixelHeight, bool inInitRef) : Object(inInitRef), mFace(inFace), mPixelHeight(inPixelHeight) { - mRotation = inRotation; mCurrentSheet = -1; } @@ -185,40 +184,19 @@ Tile Font::GetGlyph(int inCharacter,int &outAdvance) int orig_w = gw; int orig_h = gh; - switch(mRotation) - { - case gr0: break; - case gr270: - std::swap(gw,gh); - std::swap(ox,oy); - oy = -gh-oy; - break; - case gr180: - ox = -gw-ox; - oy = -gh-oy; - break; - case gr90: - std::swap(gw,gh); - std::swap(ox,oy); - ox = -gw-ox; - break; - } - while(1) { // Allocate new sheet? if (mCurrentSheet<0) { - int rows = mPixelHeight > 128 ? 1 : mPixelHeight > 64 ? 2 : mPixelHeight>32 ? 4 : 5; + int rows = mPixelHeight > 127 ? 1 : mPixelHeight > 63 ? 2 : mPixelHeight>31 ? 4 : 5; int h = 4; - while(hWantRGB() ? pfARGB : pfAlpha; Tilesheet *sheet = new Tilesheet(w,h,pf,true); sheet->GetSurface().Clear(0); @@ -226,7 +204,7 @@ Tile Font::GetGlyph(int inCharacter,int &outAdvance) mSheets.push_back(sheet); } - int tid = mSheets[mCurrentSheet]->AllocRect(gw,gh,ox,oy); + int tid = mSheets[mCurrentSheet]->AllocRect(gw,gh,ox,oy,true); if (tid>=0) { glyph.sheet = mCurrentSheet; @@ -251,50 +229,8 @@ Tile Font::GetGlyph(int inCharacter,int &outAdvance) *dest++ = 0xff; } } - else if (mRotation==gr0) - mFace->RenderGlyph(inCharacter,target); else - { - SimpleSurface *buf = new SimpleSurface(orig_w,orig_h,pfAlpha,true); - buf->IncRef(); - { - AutoSurfaceRender renderer(buf); - mFace->RenderGlyph(inCharacter,renderer.Target()); - } - - const uint8 *src; - for(int y=0; yRow(0) + buf->Width() -1 - y; - for(int x=0; xGetStride(); - } - break; - case gr180: - src = buf->Row(buf->Height()-1-y) + buf->Width() -1; - for(int x=0; xRow(buf->Height()-1) + y; - for(int x=0; xGetStride(); - } - break; - } - } - buf->DecRef(); - } + mFace->RenderGlyph(inCharacter,target); tile.mSurface->EndRender(); outAdvance = glyph.advance; @@ -327,8 +263,8 @@ void CharGroup::UpdateMetrics(TextLineMetrics &ioMetrics) mFont->UpdateMetrics(ioMetrics); } -int CharGroup::Height() -{ return mFont ? mFont->Height() : 12; } +double CharGroup::Height(double inFontToLocal) + { return inFontToLocal * (mFont ? mFont->Height() : 12); } // --- Create font from TextFormat ---------------------------------------------------- @@ -339,7 +275,7 @@ int CharGroup::Height() struct FontInfo { - FontInfo(const TextFormat &inFormat,double inScale,GlyphRotation inRotation,bool inNative) + FontInfo(const TextFormat &inFormat,double inScale,bool inNative) { name = inFormat.font; height = (int )(inFormat.size*inScale + 0.5); @@ -349,7 +285,6 @@ struct FontInfo flags |= ffBold; if (inFormat.italic) flags |= ffItalic; - rotation = inRotation; } bool operator<(const FontInfo &inRHS) const @@ -360,15 +295,12 @@ struct FontInfo if (height > inRHS.height) return false; if (!native && inRHS.native) return true; if (native && !inRHS.native) return false; - if (rotation < inRHS.rotation) return true; - if (rotation > inRHS.rotation) return false; return flags < inRHS.flags; } WString name; bool native; int height; unsigned int flags; - GlyphRotation rotation; }; @@ -377,9 +309,9 @@ FontMap sgFontMap; typedef std::map FontBytesMap; FontBytesMap sgRegisteredFonts; -Font *Font::Create(TextFormat &inFormat,double inScale,GlyphRotation inRotation,bool inNative,bool inInitRef) +Font *Font::Create(TextFormat &inFormat,double inScale,bool inNative,bool inInitRef) { - FontInfo info(inFormat,inScale,inRotation,inNative); + FontInfo info(inFormat,inScale,inNative); Font *font = 0; FontMap::iterator fit = sgFontMap.find(info); @@ -419,7 +351,7 @@ Font *Font::Create(TextFormat &inFormat,double inScale,GlyphRotation inRotation, if (!face) return 0; - font = new Font(face,info.height,inRotation,inInitRef); + font = new Font(face,info.height,inInitRef); // Store for Ron ... font->IncRef(); sgFontMap[info] = font; diff --git a/legacy/project/src/common/FreeType.cpp b/legacy/project/src/common/FreeType.cpp index cf2afb244..e9f97e6c0 100644 --- a/legacy/project/src/common/FreeType.cpp +++ b/legacy/project/src/common/FreeType.cpp @@ -345,12 +345,14 @@ bool GetFontFile(const std::string& inName,std::string &outFile) bool GetFontFile(const std::string& inName,std::string &outFile) { + const char *alternate = 0; if (!strcasecmp(inName.c_str(),"_serif") || !strcasecmp(inName.c_str(),"times.ttf") || !strcasecmp(inName.c_str(),"times")) { #if defined (ANDROID) outFile = "/system/fonts/DroidSerif-Regular.ttf"; + alternate = "/system/fonts/NotoSerif-Regular.ttf"; #elif defined (WEBOS) outFile = "/usr/share/fonts/times.ttf"; #elif defined (BLACKBERRY) @@ -405,6 +407,13 @@ bool GetFontFile(const std::string& inName,std::string &outFile) } #ifdef ANDROID + if (alternate) + { + struct stat s; + if (stat(outFile.c_str(),&s)!=0 && stat(alternate,&s)==0) + outFile = alternate; + } + //__android_log_print(ANDROID_LOG_INFO, "GetFontFile", "mapped '%s' to '%s'.", inName.c_str(), outFile.c_str()); #endif return true; diff --git a/legacy/project/src/common/Hardware.cpp b/legacy/project/src/common/Hardware.cpp index bab599efc..67e6221fc 100644 --- a/legacy/project/src/common/Hardware.cpp +++ b/legacy/project/src/common/Hardware.cpp @@ -6,7 +6,6 @@ #define M_PI 3.14159265358979323846 #endif - namespace nme { @@ -611,11 +610,59 @@ public: PushElement(); } + void PushOutline(const Vertices &inV) + { + ReserveArrays(inV.size()+1); + + //printf("PushVertices %d\n", inV.size()); + + UserPoint *v = (UserPoint *)&data.mArray[mElement.mVertexOffset]; + for(int i=0;i &inSubPolys) { + bool showTriangles = false; + if (mSolidMode && inOutline.size()<3) return; @@ -653,9 +700,12 @@ public: } } if (!isConvex) + { ConvertOutlineToTriangles(inOutline,inSubPolys); + //showTriangles = true; + } } - if (inOutline.size()<3) + if (mSolidMode && inOutline.size()<3) return; @@ -663,10 +713,18 @@ public: if (mElement.mSurface) mElement.mTexOffset = mElement.mVertexOffset + 2*sizeof(float); - PushVertices(inOutline); + if (showTriangles) + { + PushTriangleWireframe(inOutline); + //PushOutline(inOutline); + } + else + { + PushVertices(inOutline); - if (!isConvex) - data.mElements.last().mPrimType = ptTriangles; + if (!isConvex) + data.mElements.last().mPrimType = ptTriangles; + } } @@ -837,7 +895,7 @@ public: } - void removeLoops(QuickVec &curve,int startPoint,float turningPoint) + void removeLoops(QuickVec &curve,int startPoint,float turningPoint,int *inAdjustStart=0) { for(int i=startPoint;ii) + (*inAdjustStart)--; curve.erase(i,1); i--; continue; @@ -905,6 +965,12 @@ public: // replace c[i+1] with p, and erase upto and including c[j] p1 = p; curve.EraseAt(i+2,j+1); + if (inAdjustStart) + { + int &a = *inAdjustStart; + if (a>i) + a = i; + } break; } } @@ -1212,8 +1278,8 @@ public: float turnLeft = seg.isCurve() ? segJoinLeft - 0.66 : 0; float turnRight = seg.isCurve() ? segJoinRight - 0.66 : 0; - removeLoops(leftCurve,prevSegLeft,turnLeft); - removeLoops(rightCurve,prevSegRight,turnRight); + removeLoops(leftCurve,prevSegLeft,turnLeft,&segStartLeft); + removeLoops(rightCurve,prevSegRight,turnRight,&segStartRight); prevSegLeft = segStartLeft; prevSegRight = segStartRight; diff --git a/legacy/project/src/common/Surface.cpp b/legacy/project/src/common/Surface.cpp index f8fbe577b..468963fc6 100644 --- a/legacy/project/src/common/Surface.cpp +++ b/legacy/project/src/common/Surface.cpp @@ -324,7 +324,7 @@ void TTBlit( const DEST &outDest, const SRC &inSrc,const MASK &inMask, inMask.SetPos(inX , inY + y ); inSrc.SetPos( inSrcRect.x, inSrcRect.y + y ); for(int x=0;x(inMask.Mask(inSrc.Next())); #else if (!DEST_ALPHA) @@ -814,15 +814,16 @@ void SimpleSurface::BlitChannel(const RenderTarget &outTarget, const Rect &inSrc bool set_255 = (inSrcChannel==CHAN_ALPHA && !(mPixelFormat & pfHasAlpha) ); - // Translate inSrcRect src_rect to dest ... - Rect src_rect(inPosX,inPosY, inSrcRect.w, inSrcRect.h ); - // clip ... + // Start with unclipped dest rect + Rect src_rect(inSrcRect.x+inPosX,inSrcRect.x+inPosY, inSrcRect.w, inSrcRect.h ); + // Clip to dest size... src_rect = src_rect.Intersect(outTarget.mRect); - // translate back to source-coordinates ... - src_rect.Translate(inSrcRect.x-inPosX, inSrcRect.y-inPosY); - // clip to origial rect... - src_rect = src_rect.Intersect( inSrcRect ); + // Translate back to source-coordinates ... + src_rect.Translate(-inPosX, -inPosY); + + // Clip to actual source rect... + src_rect = src_rect.Intersect( Rect(0,0,Width(),Height() ) ); if (src_rect.HasPixels()) { diff --git a/legacy/project/src/common/Tessellate.cpp b/legacy/project/src/common/Tessellate.cpp index 01876314d..169bdfe78 100644 --- a/legacy/project/src/common/Tessellate.cpp +++ b/legacy/project/src/common/Tessellate.cpp @@ -1,143 +1,101 @@ #include #include #include +#include + +//#define USE_POLY2TRI + +#ifdef USE_POLY2TRI +#include "poly2tri/Poly2Tri.h" +#endif namespace nme { -struct ConcaveInfo +const double INSIDE_TOL = 1e-12; + + + + + +struct TriSearch { - ConcaveInfo() + UserPoint next; + UserPoint prev; + UserPoint min; + UserPoint max; + UserPoint p; + UserPoint v1; + UserPoint v2; + double denom; + bool isFlat; + bool isConcave; + + TriSearch(const UserPoint &inP0, const UserPoint &inPrev, const UserPoint &inNext) { - prevConcave = 0; - nextConcave = 0; - } - void init() - { - prevConcave = 0; - nextConcave = 0; - } - void makeHead() - { - prevConcave = nextConcave = this; - } + p = inP0; + next = inNext; + prev = inPrev; + v1 = next - p; + v2 = prev - p; - ConcaveInfo *prevConcave; - ConcaveInfo *nextConcave; -}; + denom = v1.Cross(v2); + isConcave = denom<0; -struct ConcaveSet -{ - ConcaveInfo head; - - ConcaveSet() - { - head.makeHead(); - } - - struct iterator - { - ConcaveInfo *info; - - inline iterator(ConcaveInfo *inInfo) : info(inInfo) { } - inline void operator++() { info = info->nextConcave; } - inline bool operator!=(const iterator &inRhs) { return info!=inRhs.info; } - - template - inline T value() { return (T)info; } - }; - - void setConcave(ConcaveInfo *info, bool inConcave) - { - // Already set? - if ((bool)(info->nextConcave) == inConcave) - return; - - if (inConcave) + if (!isConcave) { - info->nextConcave = head.nextConcave; - head.nextConcave = info; - info->prevConcave = info->nextConcave->prevConcave; - info->nextConcave->prevConcave = info; - } - else - { - info->prevConcave->nextConcave = info->nextConcave; - info->nextConcave->prevConcave = info->prevConcave; - info->nextConcave =0; - info->prevConcave =0; - } - } + isFlat = denom - bool isEar(UserPoint next, UserPoint p, UserPoint prev) - { - UserPoint v1( next - p ); - UserPoint v2( prev - p ); - - double denom = v1.Cross(v2); - - if (denom==0.0) // flat triangle - { - //printf(" -> flat\n"); - return true; - } - - UserPoint min = p; - if (next.xmax.x) max.x=next.x; - if (next.y>max.y) max.y=next.y; - if (prev.x>max.x) max.x=prev.x; - if (prev.y>max.y) max.y=prev.y; - - // TODO - speed this up - for(ConcaveInfo *info = head.nextConcave; info!=&head; info = info->nextConcave) - { - UserPoint &concave = ((T *)info)->p; - if (concave.xmax.x || concave.y>max.y) - continue; - - UserPoint v( concave - p ); - double a = v.Cross(v2); - if (a>=0 && a=0.0 && (a+b)=0) - return false; + denom -= INSIDE_TOL; + + min = p; + if (next.xmax.x) max.x=next.x; + if (next.y>max.y) max.y=next.y; + if (prev.x>max.x) max.x=prev.x; + if (prev.y>max.y) max.y=prev.y; } } - - return true; } + inline bool pointInTri(UserPoint concave) + { + UserPoint v( concave - p ); + double a = v.Cross(v2); + if (a>INSIDE_TOL && aINSIDE_TOL && (a+b)INSIDE_TOL); + } + return false; + } }; -struct EdgePoint : public ConcaveInfo + + + +struct EdgePoint { UserPoint p; EdgePoint *prev; EdgePoint *next; + bool isConcave; void init(const UserPoint &inPoint,EdgePoint *inPrev, EdgePoint *inNext) { p = inPoint; next = inNext; prev = inPrev; - ConcaveInfo::init(); + isConcave = false; } - inline bool isConcave() const { return nextConcave; } void unlink() { @@ -145,266 +103,116 @@ struct EdgePoint : public ConcaveInfo next->prev = prev; } - void calcConcave(ConcaveSet &ioConcave) + bool calcConcave() { - ioConcave.setConcave(this,Cross()>0.0); + return (prev->p - p).Cross(next->p - p) > 0.0; } - - double Cross() - { - return (prev->p - p).Cross(next->p - p); - } - - //bool convex() { return(Cross()<0.0); } }; -bool IsEar(ConcaveSet &concaveSet,EdgePoint *pi) +struct ConcaveSet { - if (concaveSet.empty()) - return true; + typedef std::multiset PointSet; + PointSet points; - if (pi->isConcave()) - return false; - - return concaveSet.isEar(pi->next->p, pi->p, pi->prev->p); - -} - -bool Intersect(UserPoint dir,UserPoint p0,UserPoint p1) -{ - // Test for simple overlap first ? - if (dir==p0 || dir==p1) - return true; - - UserPoint v = p1-p0; - double denom = dir.Cross(v); - if (denom==0) // parallel - co-linear or not + void add(EdgePoint *edge) { - if (p0.Cross(dir)!=0.0) // p0 is not on dir ... + edge->isConcave = true; + points.insert(edge->p); + } + + void remove(EdgePoint *edge) + { + edge->isConcave = false; + points.erase(edge->p); + } + + bool isEar(EdgePoint *edge) + { + if (points.empty()) + return true; + + if (edge->isConcave) return false; - // co-linear - find closest point on +ve direction on line ... - double b[2]; - if (dir.x==0) + TriSearch test(edge->p, edge->prev->p, edge->next->p); + if (test.isConcave) + return false; + if (test.isFlat) + return true; + + // TODO - maybe some quadtree style structure + PointSet::const_iterator p = points.lower_bound(test.min); + PointSet::const_iterator last = points.upper_bound(test.max); + for( ; p!=last; ++p ) { - b[0] = (double)p0.y/(double)dir.y; - b[1] = (double)p1.y/(double)dir.y; - } - else - { - b[0] = (double)p0.x/(double)dir.x; - b[1] = (double)p1.x/(double)dir.x; + UserPoint concave = *p; + // Y-bounds should be good since they are sorted by Y + if (concave.xtest.max.x ) + continue; + + if (test.pointInTri(concave)) + return false; } - int point; - if (b[0]>=0 && b[1]>=0) - point = b[1]=0) - point = 0; - else if (b[1]>=0) - point = 1; - else - point = b[1]>b[0]; - - return(b[point]>=0 && b[point]<=1.0); + return true; } - - double beta = p0.Cross(v)/denom; - if (beta<0.0 || beta>1.0) - return(false); - - // Test alpha ... - double alpha = p0.Cross(dir)/denom; - return(alpha>=0 && alpha<=1.0); -} - -bool FindDiag(ConcaveSet &concaveSet,EdgePoint *&p1,EdgePoint *&p2) -{ - for(ConcaveSet::iterator it = concaveSet.begin(); it!=concaveSet.end(); ++it) - { - EdgePoint *p = it.value(); - - UserPoint corner = p->p; - UserPoint v1( p->prev->p - corner ); - UserPoint v2( p->next->p - corner ); - for(EdgePoint *other=p->next; ; ) - { - UserPoint v( other->p-corner ); - double a = v.Cross(v2); - double b = v1.Cross(v); - if (a>=0.0 && b>=0.0) - { - // Found candidate, check for intersections ... - EdgePoint *l=p->prev; - for( ;l!=other->next;l=l->prev) - if (Intersect(v,l->p-corner,l->prev->p-corner)) - break; - if (l==other->next) - { - EdgePoint *r=p->next; - for(;l!=other->prev;r=r->next) - if (Intersect(v,r->p-corner,r->next->p-corner)) - break; - if (r==other->prev) - { - // found ! - p1 = p; - p2 = other; - return true; - } - } - } - other = other->next; - if (other == p->prev) - break; - } - } - return(false); -} - +}; void ConvertOutlineToTriangles(EdgePoint *head, int size, Vertices &outTriangles) { outTriangles.reserve( outTriangles.size() + (size-2)*3); - ConcaveSet concaveHead; + ConcaveSet concaveSet; for(EdgePoint *p = head; ; ) { - p->calcConcave(concaveHead); + if (p->calcConcave()) + concaveSet.add(p); p = p->next; if (p==head) break; } EdgePoint *pi= head; EdgePoint *p_end = pi->prev; - while(size>2) + while( pi!=p_end && size>2) { - while( pi!=p_end && size>2) + if ( concaveSet.isEar(pi) ) { - if ( IsEar(concaveHead,pi) ) - { - // Have ear triangle - yay - clip it - outTriangles.push_back(pi->prev->p); - outTriangles.push_back(pi->p); - outTriangles.push_back(pi->next->p); + // Have ear triangle - yay - clip it + outTriangles.push_back(pi->prev->p); + outTriangles.push_back(pi->p); + outTriangles.push_back(pi->next->p); - //printf(" ear : %f,%f %f,%f %f,%f\n", pi->prev->p.x, pi->prev->p.y, - //pi->p.x, pi->p.y, - //pi->next->p.x, pi->next->p.y ); + //printf(" ear : %f,%f %f,%f %f,%f\n", pi->prev->p.x, pi->prev->p.y, + //pi->p.x, pi->p.y, + //pi->next->p.x, pi->next->p.y ); - concaveHead.setConcave(pi,false); - pi->unlink(); - // Have we become concave or convex ? - pi->next->calcConcave(concaveHead); - // Has the previous one become convex ? - pi->prev->calcConcave(concaveHead); + pi->unlink(); - // Take a step back and try again... - pi = pi->prev; - p_end = pi->prev; + // Has it stopped being concave? + if (pi->next->isConcave && !pi->next->calcConcave()) + concaveSet.remove(pi->next); + // Has it stopped being concave? + if (pi->prev->isConcave && !pi->prev->calcConcave()) + concaveSet.remove(pi->prev); - size --; - } - else - pi = pi->next; - } - - if (size==3) - { - /* - printf("Triangle : %f,%f %f,%f %f,%f\n", - pi->prev->p.x, pi->prev->p.y, - pi->p.x, pi->p.y, - pi->next->p.x, pi->next->p.y ); - */ - break; - } - else if (size>2 ) - { - break; - - EdgePoint *b1=0,*b2=0; - //printf("Diag %d ?\n",size); - if ( FindDiag(concaveHead,b1,b2)) - { - // Call recursively... - /* - Vertices loop1; - loop1.reserve(size); - EdgePoint *p; - for(p=b1;p!=b2;p=p->next) - loop1.push_back(p->p); - loop1.push_back(p->p); - - ConvertOutlineToTriangles(loop1,outTriangles); - - - Vertices loop2; - loop2.reserve(size); - for(p=b2;p!=b1;p=p->next) - loop2.push_back(p->p); - loop2.push_back(p->p); - ConvertOutlineToTriangles(loop2,outTriangles); - */ - } - else - { - #if 1 - //printf("No diag?\n"); - break; - #else - // Hmmm look for "least concave" point ... - pi = p0->next->next; - double best_val = -1e99; - EdgePoint *least_concave = 0; - double smallest_val = 1e99; - EdgePoint *smallest = 0; - while(pi!=p0) - { - if (concave_points.find(pi->prev)!=concave_points.end()) - { - double cross = pi->Cross(); - if (cross>best_val) - { - best_val = cross; - least_concave = pi; - } - } - else if (!least_concave) - { - double cross = pi->Cross(); - if (crossnext; - } - - if (least_concave) - pi = least_concave; - else - pi = smallest; - - force_ear = true; - #endif - } + // Take a step back and try again... + pi = pi->prev; + p_end = pi->prev; + + size --; } + else + pi = pi->next; } } + + // --- External interface ---------- -void ReverseSubPoly(UserPoint *ioPtr,int inN) -{ - int half = inN>>1; - for(int i=0;ip.x; for(EdgePoint *in = inInner; ; ) { - for(EdgePoint *out = inOuter; ; ) + count++; + if (in->p.x < leftX) { - double dist = in->p.Dist2(out->p); - if (distnext; if (out==inOuter) break; + leftX = in->p.x; + bestIn = in; } in = in->next; if (in==inInner) break; } + double leftY = bestIn->p.y; + + // Now, shoot ray left to find outer intersection + + double closestX = -1e39; + double bestAlpha = 0.0; + EdgePoint *bestOut = 0; + EdgePoint *e0 = inOuter; + for(EdgePoint *e0 = inOuter; ; ) + { + if ( fabs(e0->p.y-leftY) < 0.0001 ) + { + if (e0->p.x<=leftX && e0->p.x>closestX) + { + bestOut = e0; + closestX = e0->p.x; + bestAlpha = 0.0; + } + } + else if ( ( (e0->p.ynext->p.y>leftY) ) || + (e0->p.y>leftY) && (e0->next->p.yp.x < leftX || e0->next->p.xp.y - leftY ) / fabs( e0->p.y - e0->next->p.y); + double x = e0->p.x + (e0->next->p.x-e0->p.x) * alpha; + if (x<=leftX && x>closestX) + { + closestX = x; + bestOut = e0; + bestAlpha = alpha; + } + } + } + + e0 = e0->next; + if (e0==inOuter) + break; + } + + if (!bestOut) + { + //printf("Could not link hole\n"); + return 0; + } + + if (bestAlpha>0.9999) + { + bestOut = bestOut->next; + } + else if (bestAlpha>0.0001) + { + // Insert node into outline + EdgePoint *b = inBuffer + 2; + b->init( UserPoint(closestX,bestOut->p.y + ( bestOut->next->p.y- bestOut->p.y) * bestAlpha), + bestOut, bestOut->next ); + + bestOut->next->prev = b; + bestOut->next = b; + + bestOut = b; + count ++; + } + inBuffer[0] = *bestOut; inBuffer[1] = *bestIn; @@ -514,29 +385,71 @@ void LinkSubPolys(EdgePoint *inOuter, EdgePoint *inInner, EdgePoint *inBuffer) bestIn->prev->next = bestIn; inBuffer[0].prev = bestIn; inBuffer[0].next->prev = inBuffer; + + return count+2; } struct SubInfo { - void calcExtent() + void set(int inP0, int inSize, UserPoint *inVertices) { - x0 = x1 = first->p.x; - y0 = y1 = first->p.y; - for(EdgePoint *p = first->next; p!=first; p = p->next ) - { - if (p->p.x < x0) x0 = p->p.x; - if (p->p.x > x1) x1 = p->p.x; - if (p->p.y < y0) y0 = p->p.y; - if (p->p.y > y1) y1 = p->p.y; - } + p0 = inP0; + size = inSize; + vertices = inVertices + p0; + + x0 = x1 = vertices[0].x; + y0 = y1 = vertices[0].y; + for(int i=1;i x1) x1 = p.x; + if (p.y < y0) y0 = p.y; + if (p.y > y1) y1 = p.y; + } } - bool contains(UserPoint inP) + + bool operator <(const SubInfo &inOther) const + { + // Extents not overlap - call it even + if (x1 <= inOther.x0 || x0>=inOther.x1 || y1 <= inOther.y0 || y0>=inOther.y1 ) + return false; + + bool allOtherInExtent = true; + for(int i=0;i=x0 && inP.x<=x1 && inP.y>=y0 && inP.y<=y1; } + UserPoint *vertices; EdgePoint *first; - EdgePoint link[2]; + EdgePoint link[3]; int group; bool is_internal; int p0; @@ -545,6 +458,11 @@ struct SubInfo float y0,y1; }; +bool sortLeft(SubInfo *a, SubInfo *b) +{ + return a->x0 < b->x0; +} + void ConvertOutlineToTriangles(Vertices &ioOutline,const QuickVec &inSubPolys) { // Order polygons ... @@ -552,22 +470,33 @@ void ConvertOutlineToTriangles(Vertices &ioOutline,const QuickVec &inSubPol if (subs<1) return; - QuickVec subInfo; + QuickVec subInfo(subs); + int bigSubs = 0; + int p0 = 0; + for(int i=0;i2 && ioOutline[p0] == ioOutline[p0+size-1]) + size--; + + if (size>2) + subInfo[bigSubs++].set(p0,size, &ioOutline[0]); + + p0 = inSubPolys[i]; + } + subInfo.resize(subs=bigSubs); + std::sort(subInfo.begin(), subInfo.end()); + + + QuickVec edges(ioOutline.size()); int index = 0; int groupId = 0; for(int sub=0;sub0?inSubPolys[sub-1]:0; - info.size = inSubPolys[sub] - info.p0; - if (ioOutline[info.p0] == ioOutline[info.p0+info.size-1]) - info.size--; - - if (info.size>2) - { UserPoint *p = &ioOutline[info.p0]; double area = 0.0; for(int i=2;i &inSubPol bool reverse = area < 0; int parent = -1; - for(int prev=subInfo.size()-1; prev>=0 && parent==-1; prev--) + for(int prev=sub-1; prev>=0 && parent==-1; prev--) { if (subInfo[prev].contains(p[0])) { @@ -608,19 +537,17 @@ void ConvertOutlineToTriangles(Vertices &ioOutline,const QuickVec &inSubPol info.first = &edges[index]; AddSubPoly(info.first,p,info.size,reverse!=info.is_internal); - if (sub holes; for(int sub=0;sub &inSubPol if (first<0) { first = sub; - size = info.size; + totalSize = size = info.size; } else { - LinkSubPolys(subInfo[first].first,info.first, info.link); - size += info.size + 2; + totalSize += info.size; + holes.push_back(&info); } } } if (first>=0) - ConvertOutlineToTriangles(subInfo[first].first, size,triangles); + { + int holeCount = holes.size(); + + #ifdef USE_POLY2TRI + p2t::Poly2Tri *poly2Tri = p2t::Poly2Tri::create(); + + std::vector< p2t::Point> pointBuffer(totalSize); + UserPoint *p = subInfo[first].vertices; + int p0 = 0; + for(int i=0;iAddSubPoly(&pointBuffer[0],size); + p0 += size; + + for(int h=0;hAddSubPoly(&pointBuffer[p0],size); + p0 += size; + } + + const std::vector< p2t::Triangle* > &tris = poly2Tri->Triangulate(); + + for(int i=0;iGetPoint(0) ); + triangles.push_back( *tri->GetPoint(1) ); + triangles.push_back( *tri->GetPoint(2) ); + } + + delete poly2Tri; + + #else + if (holeCount) + { + std::sort(holes.begin(), holes.end(), sortLeft); + + for(int h=0;h #include #include +#include #ifndef iswalpha #define iswalpha isalpha @@ -18,6 +19,8 @@ namespace nme int gPasswordChar = 42; // * +static const double GAP = 2.0; + TextField::TextField(bool inInitRef) : DisplayObject(inInitRef), alwaysShowSelection(false), antiAliasType(aaNormal), @@ -49,27 +52,32 @@ TextField::TextField(bool inInitRef) : DisplayObject(inInitRef), mStringState = ssText; mLinesDirty = true; mGfxDirty = true; - boundsWidth = 100.0; - boundsHeight = 100.0; - mActiveRect = Rect(100,100); + mTilesDirty = false; + mCaretDirty = true; + fieldWidth = 100.0; + explicitWidth = fieldWidth; + fieldHeight = 100.0; + //mActiveRect = Rect(100,100); mFontsDirty = false; mSelectMin = mSelectMax = 0; mSelectDownChar = 0; caretIndex = 0; mCaretGfx = 0; mHighlightGfx = 0; - mLastCaretHeight = -1; + mTiles = 0; mSelectKeyDown = -1; maxScrollH = 0; maxScrollV = 1; setText(L""); textWidth = 0; textHeight = 0; + fontScale = 1.0; + fontToLocal = 1.0; mLastUpDownX = -1; - mLayoutScaleH = mLayoutScaleV = -1.0; - mLayoutRotation = gr0; needsSoftKeyboard = true; mHasCaret = false; + screenGrid = false; + mBlink0 = GetTimeStamp(); } TextField::~TextField() @@ -78,67 +86,70 @@ TextField::~TextField() mCaretGfx->DecRef(); if (mHighlightGfx) mHighlightGfx->DecRef(); + if (mTiles) + mTiles->DecRef(); defaultTextFormat->DecRef(); mCharGroups.DeleteAll(); } -void TextField::setWidth(double inWidth) -{ - boundsWidth = inWidth; - mLinesDirty = true; - mGfxDirty = true; -} - double TextField::getWidth() { - /*if (autoSize != asNone) - { - if (mLinesDirty) - Layout(); - return textWidth; - } - return boundsWidth;*/ - Transform trans; - trans.mMatrix = &GetLocalMatrix(); - Extent2DF ext; - GetExtent(trans,ext,false,true); + if (mLinesDirty) + Layout(); - if (!ext.Valid()) - { - return 0; - } - - return ext.Width(); + return fieldWidth*scaleX; } double TextField::getHeight() { - /*if (autoSize != asNone) - { - if (mLinesDirty) - Layout(); - return textHeight; - } - return boundsHeight;*/ - Transform trans; - trans.mMatrix = &GetLocalMatrix(); - Extent2DF ext; - GetExtent(trans,ext,false,true); - if (!ext.Valid()) - return 0; + if (mLinesDirty) + Layout(); - return ext.Height(); + return fieldHeight*scaleY; } - +void TextField::setWidth(double inWidth) +{ + explicitWidth = inWidth; + if (autoSize==asNone || wordWrap) + { + if (scaleX!=0) + fieldWidth = inWidth/scaleX; + else + fieldWidth = 0.0; + mLinesDirty = true; + mGfxDirty = true; + } + mDirtyFlags |= dirtLocalMatrix; +} void TextField::setHeight(double inHeight) { - boundsHeight = inHeight; - mLinesDirty = true; - mGfxDirty = true; + if (autoSize==asNone) + { + if (scaleY!=0) + fieldHeight = inHeight/scaleY; + else + fieldHeight = 0.0; + + fieldHeight = inHeight; + mLinesDirty = true; + mGfxDirty = true; + } } +void TextField::modifyLocalMatrix(Matrix &ioMatrix) +{ + if ( (autoSize==asCenter || autoSize==asRight) && !multiline ) + { + if (autoSize==asCenter) + ioMatrix.mtx -= (fieldWidth-explicitWidth) * 0.5; + else + ioMatrix.mtx -= (fieldWidth-explicitWidth); + } +} + + const TextFormat *TextField::getDefaultTextFormat() { @@ -178,12 +189,12 @@ void TextField::SplitGroup(int inGroup,int inPos) TextFormat *TextField::getTextFormat(int inStart,int inEnd) { TextFormat *commonFormat = NULL; - + for(int i=0;imFormat; - + if (commonFormat == NULL) { commonFormat = new TextFormat (*format); @@ -234,21 +245,21 @@ void TextField::setTextFormat(TextFormat *inFmt,int inStart,int inEnd) { if (!inFmt) return; - + Layout(); - + int max = mCharPos.size(); - + if (inStart<0) { inStart = 0; - inEnd = max; + inEnd = max; } else if (inEnd<0) { inEnd = inStart + 1; } - + if (inEnd>max) inEnd = max; if (inEnd<=inStart) @@ -298,6 +309,8 @@ void TextField::setTextColor(int inCol) defaultTextFormat = defaultTextFormat->COW(); defaultTextFormat->color = inCol; mGfxDirty = true; + mTilesDirty = true; + mCaretDirty = true; DirtyCache(); } } @@ -353,24 +366,26 @@ void TextField::setWordWrap(bool inWordWrap) void TextField::setAutoSize(int inAutoSize) { - autoSize = (AutoSizeMode)inAutoSize; - mLinesDirty = true; - mGfxDirty = true; - DirtyCache(); + if (inAutoSize!=autoSize) + { + autoSize = (AutoSizeMode)inAutoSize; + mLinesDirty = true; + mGfxDirty = true; + mDirtyFlags |= dirtLocalMatrix; + DirtyCache(); + } } double TextField::getTextHeight() { Layout(); - double h = textHeight/mLayoutScaleV; - return std::max(h-4,0.0); + return textHeight; } double TextField::getTextWidth() { Layout(); - double w = textWidth/mLayoutScaleH; - return std::max(w-4,0.0); + return textWidth; } @@ -384,8 +399,8 @@ bool TextField::FinishEditOnEnter() int TextField::getBottomScrollV() { Layout(); - int l = std::max(scrollV -1,0); - int height = boundsHeight; + double l = std::max(scrollV -1,0); + double height = fieldHeight-2*GAP; while(height>0 && lmaxScrollH) scrollH = maxScrollH; - // TODO: do we need to re-layout on scroll? - mLinesDirty = true; - mGfxDirty = true; - DirtyCache(); + + if (oldPos!=scrollH) + { + mTilesDirty = true; + mCaretDirty = true; + mGfxDirty = true; + DirtyCache(); + } } void TextField::setScrollV(int inScrollV) { + setScrollVClearSel(inScrollV,true); +} + +void TextField::setScrollVClearSel(int inScrollV,bool inClearSel) +{ + int oldPos = scrollV; if (inScrollV<1) inScrollV = 1; if (inScrollV>maxScrollV) inScrollV =maxScrollV; - if (inScrollV!=scrollV && mSelectMin!=mSelectMax) + if (inScrollV!=oldPos) { - mSelectMin = mSelectMax = 0; - mSelectKeyDown = -1; + if (inClearSel && mSelectMin!=mSelectMax) + { + mSelectMin = mSelectMax = 0; + mSelectKeyDown = -1; + } + + scrollV = inScrollV; + + mTilesDirty = true; + mCaretDirty = true; + mGfxDirty = true; + DirtyCache(); } - - scrollV = inScrollV; - // TODO: do we need to re-layout on scroll? - mLinesDirty = true; - mGfxDirty = true; - DirtyCache(); - } -int TextField::getLength() +int TextField::getLength() const { if (mLines.empty()) return 0; - Line & l = mLines[ mLines.size()-1 ]; + const Line & l = mLines[ mLines.size()-1 ]; return l.mChar0 + l.mChars; } -int TextField::PointToChar(int inX,int inY) +int TextField::PointToChar(UserPoint inPoint) const { - if (mCharPos.empty()) + if (mCharPos.empty() || inPoint.y inY && line.mChars) + const Line &line = mLines[l]; + //printf("%d] %f/%f/%f\n",l, line.mY0, inPoint.y, line.mY0+line.mMetrics.height ); + if ( (line.mY0+line.mMetrics.height) > inPoint.y && line.mChars) { + if (line.mChars==1) + return line.mChar0; + // Find the char for(int c=0; cinX) - return c==0 ? line.mChar0 : line.mChar0+c-1; + { + double nextX = cEnablePopupKeyboard(true); - UserPoint point = TargetToRect(GetFullMatrix(true), UserPoint( inEvent.x, inEvent.y) ); - int pos = PointToChar(point.x,point.y); + UserPoint point = GlobalToLocal(UserPoint( inEvent.x, inEvent.y)); + int pos = PointToChar(point); caretIndex = pos; if (selectable) { mSelectDownChar = pos; mSelectMin = mSelectMax = pos; + mTilesDirty = true; + mCaretDirty = true; mGfxDirty = true; DirtyCache(); } @@ -501,42 +539,14 @@ bool TextField::CaptureDown(Event &inEvent) return true; } -UserPoint TextField::TargetToRect(const Matrix &inMatrix,const UserPoint &inPoint) -{ - UserPoint p = (inPoint - UserPoint(inMatrix.mtx, inMatrix.mty)); - switch(mLayoutRotation) - { - case gr0: break; - case gr90: return UserPoint(p.y,-p.x); - case gr180: return UserPoint(-p.x,-p.y); - case gr270: return UserPoint(-p.y,p.x); - } - return p; -} - -UserPoint TextField::RectToTarget(const Matrix &inMatrix,const UserPoint &inPoint) -{ - UserPoint trans(inMatrix.mtx, inMatrix.mty); - switch(mLayoutRotation) - { - case gr0: break; - case gr90: return UserPoint(-inPoint.y,inPoint.x) + trans; - case gr180: return UserPoint(-inPoint.x,-inPoint.y) + trans; - case gr270: return UserPoint(inPoint.y,-inPoint.x) + trans; - } - return inPoint + trans; - -} - - - void TextField::Drag(Event &inEvent) { if (selectable) { mSelectKeyDown = -1; - UserPoint point = TargetToRect(GetFullMatrix(true),UserPoint( inEvent.x, inEvent.y) ); - int pos = PointToChar(point.x,point.y); + UserPoint point = GlobalToLocal(UserPoint( inEvent.x, inEvent.y)); + + int pos = PointToChar(point); if (pos>mSelectDownChar) { mSelectMin = mSelectDownChar; @@ -547,22 +557,24 @@ void TextField::Drag(Event &inEvent) mSelectMin = pos; mSelectMax = mSelectDownChar; } - if (point.x>mActiveRect.x1()) - { - scrollH+=(point.x-mActiveRect.x1()); - if (scrollH>maxScrollH) - scrollH = maxScrollH; - } - else if (point.xfieldWidth-GAP) + setScrollH(scrollH+(point.x-(fieldWidth-GAP))); + else if (point.xfieldHeight-GAP) + setScrollVClearSel(scrollV+1,false); + else if (point.y %d,%d\n", pos, mSelectDownChar, mSelectMin , mSelectMax); mGfxDirty = true; + mTilesDirty = true; + mCaretDirty = true; DirtyCache(); } } @@ -613,7 +625,7 @@ void TextField::OnKey(Event &inEvent) ShowCaret(); OnChange(); return; - + case keyDELETE: if (mSelectMin0) caretIndex--; break; @@ -672,6 +689,8 @@ void TextField::OnKey(Event &inEvent) mSelectMin = std::min(mSelectKeyDown,caretIndex); mSelectMax = std::max(mSelectKeyDown,caretIndex); mGfxDirty = true; + mTilesDirty = true; + mCaretDirty = true; } ShowCaret(); return; @@ -707,63 +726,48 @@ void TextField::OnKey(Event &inEvent) void TextField::ShowCaret(bool inFromDrag) { - if (!CaretOn()) - return; - ImagePoint pos(0,0); - bool changed = false; + mCaretDirty = true; + mBlink0 = GetTimeStamp(); + mHasCaret = false; - if (caretIndex < mCharPos.size()) - pos = mCharPos[caretIndex]; - else if (mLines.size()) + UserPoint pos = GetCursorPos() - GetScrollPos(); + // When dragging, allow caret to be hidden off to the left + if (pos.xfieldWidth-GAP) + setScrollH( scrollH + pos.x - (fieldWidth-GAP) + 1 ); + + int line = LineFromChar(caretIndex); + if (pos.y= mActiveRect.w) + else if (pos.y+mLines[line].mMetrics.height > fieldHeight-GAP) { - changed = true; - scrollH = pos.x - mActiveRect.w + 1; + int scroll = scrollV-1; + double extra = pos.y+mLines[line].mMetrics.height - (fieldHeight-GAP); + while(extra>0 && scroll