зеркало из https://github.com/mozilla/pjs.git
Bug 299239 - BiDi: Caret is stuck when reaching a single LTR character in RTL text. patch from Eyal Rozenberg <eyalroz@technion.ac.il>. r+sr=roc
This commit is contained in:
Родитель
5d8f7afa5c
Коммит
a69be7ab0f
|
@ -4312,15 +4312,81 @@ NS_IMETHODIMP
|
||||||
nsTextFrame::PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos)
|
nsTextFrame::PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos)
|
||||||
{
|
{
|
||||||
#ifdef IBMBIDI
|
#ifdef IBMBIDI
|
||||||
|
// XXX TODO: - this explanation may not be accurate
|
||||||
|
// - need to better explain what aPos->mPreferLeft means
|
||||||
|
// for two frames on the same line
|
||||||
|
// - need to better explain for what happens when you move
|
||||||
|
// from the end of a line to the beginning of the next
|
||||||
|
// despite not making a move logically within the text.
|
||||||
|
// - need to explain why XORing with the embedding level
|
||||||
|
// achieves the desired effect
|
||||||
|
//
|
||||||
|
// When you move your caret by in some visual direction, and you find the
|
||||||
|
// new position is at the edge of a line (beginning or end), you want to
|
||||||
|
// stay on the current line rather than move to the next or the previous
|
||||||
|
// one.
|
||||||
|
//
|
||||||
|
// When your frames are ordered the same way logically as they are
|
||||||
|
// visually (e.g. when they're frames of LTR text displayed in
|
||||||
|
// left-then-down order), this translates to terms of 'prefer left' or
|
||||||
|
// 'prefer right', i.e. if you move to the left and hit the beginning of
|
||||||
|
// the line you have to choose between the frame "on the left" - the last
|
||||||
|
// frame on the previous line - rather than the frame "on the right" - the
|
||||||
|
// first frame of the text line. So you could say that you always prefer
|
||||||
|
// the frame closer to where you are now, i.e. the frame opposite to the
|
||||||
|
// movement.
|
||||||
|
// (see nsSelection::MoveCaret() for an example in code)
|
||||||
|
//
|
||||||
|
// When the frames are displayed in right-then-down order (i.e. frames
|
||||||
|
// within an RTL line), the last sentence remains correct, but now
|
||||||
|
// the directions are reversed - if you're moving left, the frame
|
||||||
|
// closer to you is the one "on the right" - the last frame on the current
|
||||||
|
// line - rather than the one "on the left" - the first frame on the next
|
||||||
|
// line. Now, although this implementation of this method (i.e.
|
||||||
|
// for nsTextFrame) has no use itself for aPos->mPreferLeft, we may
|
||||||
|
// be delegating the PeekOffset to another frame, and nsFrame's
|
||||||
|
// implementation does use aPos->mPreferLeft, so we have to set it
|
||||||
|
// here (why?), and we have to maintain consistency along calls
|
||||||
|
// to PeekOffset of nsTextFrames on the same line.
|
||||||
|
//
|
||||||
|
// Note:
|
||||||
|
// eDirPrevious actually means 'in the visual left-then-down direction'
|
||||||
|
// eDirNext actually means 'in the visual right-then-down direction'
|
||||||
|
// (this is again due to the LTRish bias of the nomenclature)
|
||||||
|
|
||||||
PRBool isOddLevel = NS_GET_EMBEDDING_LEVEL(this) & 1;
|
PRBool isOddLevel = NS_GET_EMBEDDING_LEVEL(this) & 1;
|
||||||
|
|
||||||
if ((eSelectCharacter == aPos->mAmount)
|
if ((eSelectCharacter == aPos->mAmount)
|
||||||
|| (eSelectWord == aPos->mAmount))
|
|| (eSelectWord == aPos->mAmount))
|
||||||
aPos->mPreferLeft ^= isOddLevel;
|
aPos->mPreferLeft = (eDirPrevious == aPos->mDirection) ^ isOddLevel;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!aPos || !mContent)
|
if (!aPos || !mContent)
|
||||||
return NS_ERROR_NULL_POINTER;
|
return NS_ERROR_NULL_POINTER;
|
||||||
|
|
||||||
|
// XXX TODO: explain this policy; why the assymetry between
|
||||||
|
// too high and too low start offsets?
|
||||||
|
//
|
||||||
|
// There are 4 possible ranges from aPos->mStartOffset:
|
||||||
|
//
|
||||||
|
// 0 mContentLength mContentLength+mContentLength
|
||||||
|
// | | |
|
||||||
|
// range #1 | range #2 | range #3 | range #4
|
||||||
|
// ***************
|
||||||
|
// our frame's part
|
||||||
|
// of the content
|
||||||
|
//
|
||||||
|
// Range Policy
|
||||||
|
// ------------------------------------------------------------------------
|
||||||
|
// #1 Assume the start position is at the end of the content of 'this'
|
||||||
|
// #2 Round up the position to the beginning of our frame
|
||||||
|
// #3 No change necessary
|
||||||
|
// #4 Delegate the PeekOffset() to the next frame (according
|
||||||
|
// to the direction, either to our left or our right)
|
||||||
|
//
|
||||||
|
// Note: the diagram above is drawn left-to-right, but advancing
|
||||||
|
// in the content may sometimes mean going right-to-left
|
||||||
|
|
||||||
if (aPos->mStartOffset < 0 )
|
if (aPos->mStartOffset < 0 )
|
||||||
aPos->mStartOffset = mContentLength + mContentOffset;
|
aPos->mStartOffset = mContentLength + mContentOffset;
|
||||||
if (aPos->mStartOffset < mContentOffset){
|
if (aPos->mStartOffset < mContentOffset){
|
||||||
|
@ -4344,6 +4410,13 @@ nsTextFrame::PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos)
|
||||||
return nextInFlow->PeekOffset(aPresContext, aPos);
|
return nextInFlow->PeekOffset(aPresContext, aPos);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// XXX TODO: explain the following:
|
||||||
|
// - if this frame is the first of the last in the line according
|
||||||
|
// to the content indices, why not handle the cases of
|
||||||
|
// eSelectBeginLine/eSelectEndLine
|
||||||
|
// - why can't we make the hand-off to the parent class' method
|
||||||
|
// before the of aPos->mStartOffset and aPos->mPreferLeft?
|
||||||
|
|
||||||
if (aPos->mAmount == eSelectLine || aPos->mAmount == eSelectBeginLine
|
if (aPos->mAmount == eSelectLine || aPos->mAmount == eSelectBeginLine
|
||||||
|| aPos->mAmount == eSelectEndLine || aPos->mAmount == eSelectParagraph)
|
|| aPos->mAmount == eSelectEndLine || aPos->mAmount == eSelectParagraph)
|
||||||
{
|
{
|
||||||
|
@ -4531,6 +4604,10 @@ nsTextFrame::PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos)
|
||||||
*/
|
*/
|
||||||
if (i > mContentLength){
|
if (i > mContentLength){
|
||||||
found = PR_FALSE;
|
found = PR_FALSE;
|
||||||
|
// XXX TODO: explain why in this case GetNextInFlow() is good enough,
|
||||||
|
// but in the case of
|
||||||
|
// aPos->mStartOffset > (mContentOffset + mContentLength)
|
||||||
|
// above, we use the presentation context and get the 'nextBidi'
|
||||||
frameUsed = GetNextInFlow();
|
frameUsed = GetNextInFlow();
|
||||||
start = mContentOffset + mContentLength;
|
start = mContentOffset + mContentLength;
|
||||||
aPos->mContentOffset = start;//in case next call fails we stop at this offset
|
aPos->mContentOffset = start;//in case next call fails we stop at this offset
|
||||||
|
@ -4686,12 +4763,18 @@ nsTextFrame::PeekOffset(nsPresContext* aPresContext, nsPeekOffsetStruct *aPos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TryNextFrame:
|
TryNextFrame:
|
||||||
|
// XXX TODO: explain why in this case GetNextInFlow() is good enough,
|
||||||
|
// but in the case of
|
||||||
|
// aPos->mStartOffset > (mContentOffset + mContentLength)
|
||||||
|
// above, we use the presentation context and get the 'nextBidi'
|
||||||
frameUsed = GetNextInFlow();
|
frameUsed = GetNextInFlow();
|
||||||
start = 0;
|
start = 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!found || (aPos->mContentOffset > (mContentOffset + mContentLength)) || (aPos->mContentOffset < mContentOffset))
|
if (!found ||
|
||||||
|
(aPos->mContentOffset > (mContentOffset + mContentLength)) ||
|
||||||
|
(aPos->mContentOffset < mContentOffset))
|
||||||
{
|
{
|
||||||
aPos->mContentOffset = PR_MIN(aPos->mContentOffset, mContentOffset + mContentLength);
|
aPos->mContentOffset = PR_MIN(aPos->mContentOffset, mContentOffset + mContentLength);
|
||||||
aPos->mContentOffset = PR_MAX(aPos->mContentOffset, mContentOffset);
|
aPos->mContentOffset = PR_MAX(aPos->mContentOffset, mContentOffset);
|
||||||
|
|
Загрузка…
Ссылка в новой задаче