第二人生的源码分析(九十二)LLLineEditor实现编辑框
在基于GUI界面的软件交互时,最基本的输入就是编辑框了,比如下图里输入的用户名称和密码,在游戏界面的编辑框里输入文字是一个高技术难度的问题,因为Windows输入法调用,也是难点之一。
那么在第二人生里的用户名称和密码的编辑框是怎么样实现的呢?现在就来分析这部份的代码,它的类继承关系如下:
class LLLineEditor
: public LLUICtrl, public LLEditMenuHandler, protected LLPreeditor
LLLineEditor继续了LLUICtrl类,说明它是一个控件类。它的主要显示代码如下:
#001 void LLLineEditor::draw()
#002 {
判断不能显示时就直接返回去。
#003 if( !getVisible() )
#004 {
#005 return;
#006 }
#007
获取显示字符串的长度。
#008 S32 text_len = mText.length();
#009
如果是密码的方式,就只显示为星字符串。
#010 LLString saved_text;
#011 if (mDrawAsterixes)
#012 {
#013 saved_text = mText.getString();
#014 LLString text;
#015 for (S32 i = 0; i < mText.length(); i++)
#016 {
#017 text += '*';
#018 }
#019 mText = text;
#020 }
#021
设置背景框的大小。
#022 // draw rectangle for the background
#023 LLRect background( 0, getRect().getHeight(), getRect().getWidth(), 0 );
根据边框的大小设置合适的显示背景。
#024 background.stretch( -mBorderThickness );
#025
#026 LLColor4 bg_color = mReadOnlyBgColor;
#027
显示背景的颜色。
#028 // drawing solids requires texturing be disabled
#029 {
#030 LLGLSNoTexture no_texture;
#031 // draw background for text
#032 if( !mReadOnly )
#033 {
#034 if( gFocusMgr.getKeyboardFocus() == this )
#035 {
#036 bg_color = mFocusBgColor;
#037 }
#038 else
#039 {
#040 bg_color = mWriteableBgColor;
#041 }
#042 }
#043 gl_rect_2d(background, bg_color);
#044 }
#045
#046 // draw text
#047
开始显示字符串。
#048 S32 cursor_bottom = background.mBottom + 1;
#049 S32 cursor_top = background.mTop - 1;
#050
获取字体显示的颜色。
#051 LLColor4 text_color;
#052 if (!mReadOnly)
#053 {
#054 if (!getTentative())
#055 {
#056 text_color = mFgColor;
#057 }
#058 else
#059 {
#060 text_color = mTentativeFgColor;
#061 }
#062 }
#063 else
#064 {
#065 text_color = mReadOnlyFgColor;
#066 }
#067 LLColor4 label_color = mTentativeFgColor;
#068
显示预先编辑的标志。
#069 if (hasPreeditString())
#070 {
#071 // Draw preedit markers. This needs to be before drawing letters.
#072 for (U32 i = 0; i < mPreeditStandouts.size(); i++)
#073 {
#074 const S32 preedit_left = mPreeditPositions[i];
#075 const S32 preedit_right = mPreeditPositions[i + 1];
#076 if (preedit_right > mScrollHPos)
#077 {
#078 S32 preedit_pixels_left = findPixelNearestPos(llmax(preedit_left, mScrollHPos) - getCursor());
#079 S32 preedit_pixels_right = llmin(findPixelNearestPos(preedit_right - getCursor()), background.mRight);
#080 if (preedit_pixels_left >= background.mRight)
#081 {
#082 break;
#083 }
#084 if (mPreeditStandouts[i])
#085 {
#086 gl_rect_2d(preedit_pixels_left + PREEDIT_STANDOUT_GAP,
#087 background.mBottom + PREEDIT_STANDOUT_POSITION,
#088 preedit_pixels_right - PREEDIT_STANDOUT_GAP - 1,
#089 background.mBottom + PREEDIT_STANDOUT_POSITION -
#090 PREEDIT_STANDOUT_THICKNESS,
#091 (text_color * PREEDIT_STANDOUT_BRIGHTNESS + bg_color * (1 -
#092 PREEDIT_STANDOUT_BRIGHTNESS)).setAlpha(1.0f));
#093 }
#094 else
#095 {
#096 gl_rect_2d(preedit_pixels_left + PREEDIT_MARKER_GAP,
#097 background.mBottom + PREEDIT_MARKER_POSITION,
#098 preedit_pixels_right - PREEDIT_MARKER_GAP - 1,
#099 background.mBottom + PREEDIT_MARKER_POSITION -
#100 PREEDIT_MARKER_THICKNESS,
#101 (text_color * PREEDIT_MARKER_BRIGHTNESS + bg_color * (1 -
#102 PREEDIT_MARKER_BRIGHTNESS)).setAlpha(1.0f));
#103 }
#104 }
#105 }
#106 }
#107
#108 S32 rendered_text = 0;
#109 F32 rendered_pixels_right = (F32)mMinHPixels;
#110 F32 text_bottom = (F32)background.mBottom + (F32)UI_LINEEDITOR_V_PAD;
#111
显示选中的效果。
#112 if( (gFocusMgr.getKeyboardFocus() == this) && hasSelection() )
#113 {
#114 S32 select_left;
#115 S32 select_right;
#116 if( mSelectionStart < getCursor() )
#117 {
#118 select_left = mSelectionStart;
#119 select_right = getCursor();
#120 }
#121 else
#122 {
#123 select_left = getCursor();
#124 select_right = mSelectionStart;
#125 }
#126
#127 if( select_left > mScrollHPos )
#128 {
#129 // unselected, left side
#130 rendered_text = mGLFont->render(
#131 mText, mScrollHPos,
#132 rendered_pixels_right, text_bottom,
#133 text_color,
#134 LLFontGL::LEFT, LLFontGL::BOTTOM,
#135 LLFontGL::NORMAL,
#136 select_left - mScrollHPos,
#137 mMaxHPixels - llround(rendered_pixels_right),
#138 &rendered_pixels_right);
#139 }
#140
#141 if( (rendered_pixels_right < (F32)mMaxHPixels) && (rendered_text < text_len) )
#142 {
#143 LLColor4 color(1.f - bg_color.mV[0], 1.f - bg_color.mV[1], 1.f - bg_color.mV[2], 1.f);
#144 // selected middle
#145 S32 width = mGLFont->getWidth(mText.getWString().c_str(), mScrollHPos + rendered_text, select_right - mScrollHPos - rendered_text);
#146 width = llmin(width, mMaxHPixels - llround(rendered_pixels_right));
#147 gl_rect_2d(llround(rendered_pixels_right), cursor_top, llround(rendered_pixels_right)+width, cursor_bottom, color);
#148
显示相应选中的字符串。
#149 rendered_text += mGLFont->render(
#150 mText, mScrollHPos + rendered_text,
#151 rendered_pixels_right, text_bottom,
#152 LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], 1 ),
#153 LLFontGL::LEFT, LLFontGL::BOTTOM,
#154 LLFontGL::NORMAL,
#155 select_right - mScrollHPos - rendered_text,
#156 mMaxHPixels - llround(rendered_pixels_right),
#157 &rendered_pixels_right);
#158 }
#159
下面开始显示没有选中的字符串。
#160 if( (rendered_pixels_right < (F32)mMaxHPixels) && (rendered_text < text_len) )
#161 {
#162 // unselected, right side
#163 mGLFont->render(
#164 mText, mScrollHPos + rendered_text,
#165 rendered_pixels_right, text_bottom,
#166 text_color,
#167 LLFontGL::LEFT, LLFontGL::BOTTOM,
#168 LLFontGL::NORMAL,
#169 S32_MAX,
#170 mMaxHPixels - llround(rendered_pixels_right),
#171 &rendered_pixels_right);
#172 }
#173 }
#174 else
#175 {
#176 mGLFont->render(
#177 mText, mScrollHPos,
#178 rendered_pixels_right, text_bottom,
#179 text_color,
#180 LLFontGL::LEFT, LLFontGL::BOTTOM,
#181 LLFontGL::NORMAL,
#182 S32_MAX,
#183 mMaxHPixels - llround(rendered_pixels_right),
#184 &rendered_pixels_right);
#185 }
#186
如果正在编辑过程中,设置IME在合适的位置。
#187 // If we're editing...
#188 if( gFocusMgr.getKeyboardFocus() == this)
#189 {
#190 // (Flash the cursor every half second)
#191 if (gShowTextEditCursor && !mReadOnly)
#192 {
#193 F32 elapsed = mKeystrokeTimer.getElapsedTimeF32();
#194 if( (elapsed < CURSOR_FLASH_DELAY ) || (S32(elapsed * 2) & 1) )
#195 {
#196 S32 cursor_left = findPixelNearestPos();
#197 cursor_left -= UI_LINEEDITOR_CURSOR_THICKNESS / 2;
#198 S32 cursor_right = cursor_left + UI_LINEEDITOR_CURSOR_THICKNESS;
#199 if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
#200 {
#201 const LLWString space(utf8str_to_wstring(LLString(" ")));
#202 S32 wswidth = mGLFont->getWidth(space.c_str());
#203 S32 width = mGLFont->getWidth(mText.getWString().c_str(), getCursor(), 1) + 1;
#204 cursor_right = cursor_left + llmax(wswidth, width);
#205 }
#206 // Use same color as text for the Cursor
#207 gl_rect_2d(cursor_left, cursor_top,
#208 cursor_right, cursor_bottom, text_color);
#209 if (LL_KIM_OVERWRITE == gKeyboard->getInsertMode() && !hasSelection())
#210 {
#211 mGLFont->render(mText, getCursor(), (F32)(cursor_left + UI_LINEEDITOR_CURSOR_THICKNESS / 2),
#212 text_bottom,
#213 LLColor4( 1.f - text_color.mV[0], 1.f - text_color.mV[1], 1.f - text_color.mV[2], 1 ),
#214 LLFontGL::LEFT, LLFontGL::BOTTOM,
#215 LLFontGL::NORMAL,
#216 1);
#217 }
#218
#219 // Make sure the IME is in the right place
#220 S32 pixels_after_scroll = findPixelNearestPos(); // RCalculcate for IME position
#221 LLRect screen_pos = getScreenRect();
#222 LLCoordGL ime_pos( screen_pos.mLeft + pixels_after_scroll, screen_pos.mTop - UI_LINEEDITOR_V_PAD );
#223
#224 ime_pos.mX = (S32) (ime_pos.mX * LLUI::sGLScaleFactor.mV[VX]);
#225 ime_pos.mY = (S32) (ime_pos.mY * LLUI::sGLScaleFactor.mV[VY]);
#226 getWindow()->setLanguageTextInput( ime_pos );
#227 }
#228 }
#229
显示子窗口。
#230 // Draw children (border)
#231 //mBorder->setVisible(TRUE);
#232 mBorder->setKeyboardFocusHighlight( TRUE );
#233 LLView::draw();
#234 mBorder->setKeyboardFocusHighlight( FALSE );
#235 //mBorder->setVisible(FALSE);
#236 }
#237 else // does not have keyboard input
#238 {
没有字符串,就显示为一个标签。
#239 // draw label if no text provided
#240 if (0 == mText.length())
#241 {
#242 mGLFont->render(mLabel.getWString(), 0,
#243 LABEL_HPAD, (F32)text_bottom,
#244 label_color,
#245 LLFontGL::LEFT, LLFontGL::BOTTOM,
#246 LLFontGL::NORMAL,
#247 S32_MAX,
#248 mMaxHPixels - llround(rendered_pixels_right),
#249 &rendered_pixels_right, FALSE);
#250 }
#251 // Draw children (border)
#252 LLView::draw();
#253 }
#254
保存相应的密码字符串。
#255 if (mDrawAsterixes)
#256 {
#257 mText = saved_text;
#258 }
#259 }
上面的函数先设置背景的颜色,并且把背景显示,然后根据是否为密码,或者选择字符串来作合适的处理,最后显示相应的字符串,并且输入法窗口在合适的位置上。