diff --git a/code/nel/include/nel/gui/interface_element.h b/code/nel/include/nel/gui/interface_element.h index 1a360bbc6..540ba96e8 100644 --- a/code/nel/include/nel/gui/interface_element.h +++ b/code/nel/include/nel/gui/interface_element.h @@ -38,6 +38,16 @@ namespace NLGUI class IActionHandler; class CGroupParagraph; + /** + * Interface for UI scale change event + */ + class IInterfaceScaleWatcher + { + public: + virtual ~IInterfaceScaleWatcher(){} + virtual void onInterfaceScaleChanged()=0; + }; + /** * A visitor to walk a tree of interface elements and apply a teartment on them. * @@ -66,7 +76,7 @@ namespace NLGUI * \author Nevrax France * \date 2002 */ - class CInterfaceElement : public CReflectableRefPtrTarget, public NLMISC::IStreamable + class CInterfaceElement : public IInterfaceScaleWatcher, public CReflectableRefPtrTarget, public NLMISC::IStreamable { public: @@ -409,6 +419,10 @@ namespace NLGUI */ virtual void onInvalidateContent() {} + /* Element UI scale change event callback + */ + virtual void onInterfaceScaleChanged() {} + // called by interfaceManager for master window only void resetInvalidCoords(); diff --git a/code/nel/include/nel/gui/view_renderer.h b/code/nel/include/nel/gui/view_renderer.h index 3c643bf39..6a09a085b 100644 --- a/code/nel/include/nel/gui/view_renderer.h +++ b/code/nel/include/nel/gui/view_renderer.h @@ -176,6 +176,13 @@ namespace NLGUI */ void getScreenOOSize (float &oow, float &ooh); + /* + * UI scaling + */ + void setInterfaceScale(float scale, sint32 width = 0, sint32 height = 0); + float getInterfaceScale() const { return _InterfaceScale; } + void setBilinearFiltering(bool b) { _Bilinear = b; } + /* * is the Screen minimized? */ @@ -185,7 +192,7 @@ namespace NLGUI * drawBitmap : this is the interface with all the views * */ - void drawRotFlipBitmap (sint layerId, sint32 x, sint32 y, sint32 width, sint32 height, uint8 rot, bool flipv, + void drawRotFlipBitmap (sint layerId, float x, float y, float width, float height, uint8 rot, bool flipv, sint32 nTxId, const NLMISC::CRGBA &col = NLMISC::CRGBA(255,255,255,255)); /* @@ -526,6 +533,14 @@ namespace NLGUI float _OneOverScreenW, _OneOverScreenH; bool _IsMinimized; + // UI scaling + float _InterfaceScale; + float _InterfaceUserScale; + sint32 _InterfaceBaseW, _InterfaceBaseH; + sint32 _EffectiveScreenW, _EffectiveScreenH; + bool _Bilinear; + + void updateInterfaceScale(); //map linking a uint to a bitmap. Used to display figurs std::vector _IndexesToTextureIds; @@ -596,7 +611,6 @@ namespace NLGUI /// Set of hw cursor images static std::set< std::string > *hwCursors; static float hwCursorScale; - }; diff --git a/code/nel/include/nel/gui/view_text.h b/code/nel/include/nel/gui/view_text.h index cf53b5207..aa1d83330 100644 --- a/code/nel/include/nel/gui/view_text.h +++ b/code/nel/include/nel/gui/view_text.h @@ -70,6 +70,7 @@ namespace NLGUI virtual void checkCoords(); virtual void updateCoords(); virtual void onAddToGroup(); + virtual void onInterfaceScaleChanged(); /// From CInterfaceElement sint32 getMaxUsedW() const; @@ -90,6 +91,7 @@ namespace NLGUI void setShadowColor (const NLMISC::CRGBA &color); void setShadowOffset (sint x, sint y); void setLineMaxW (sint nMaxW, bool invalidate=true); + void setOverflowText(const ucstring &text) { _OverflowText = text; } void setMultiLine (bool bMultiLine); void setMultiLineSpace (sint nMultiLineSpace); void setMultiLineMaxWOnly (bool state); @@ -115,6 +117,7 @@ namespace NLGUI NLMISC::CRGBA getShadowColor() { return _ShadowColor; } void getShadowOffset(sint &x, sint &y) { x = _ShadowX; y = _ShadowY; } sint getLineMaxW() const { return _LineMaxW; } + ucstring getOverflowText() const { return _OverflowText; } bool getMultiLine() const { return _MultiLine; } sint getMultiLineSpace() const { return _MultiLineSpace; } bool getMultiLineMaxWOnly() const { return _MultiLineMaxWOnly; } @@ -128,6 +131,8 @@ namespace NLGUI uint getFontHeight() const; // get current font leg height, in pixels uint getFontLegHeight() const; + // get current line height, in pixels + float getLineHeight() const; // Set the display mode (supported with multiline only for now) void setTextMode(TTextMode mode); TTextMode getTextMode() const { return _TextMode; } @@ -148,11 +153,11 @@ namespace NLGUI * When looking at standard edit box, we see that if a line is split accross to line with no * This also returns the height of the line */ - void getCharacterPositionFromIndex(sint index, bool lineEnd, sint &x, sint &y, sint &height) const; + void getCharacterPositionFromIndex(sint index, bool lineEnd, float &x, float &y, float &height) const; /** From a coordinate relative to the BR BR corner of the text, return the index of a character. * If no character is found at the given position, the closest character is returned (first or last character, for the line or the whole text) */ - void getCharacterIndexFromPosition(sint x, sint y, uint &index, bool &lineEnd) const; + void getCharacterIndexFromPosition(float x, float y, uint &index, bool &lineEnd) const; /** From a character index, get the index of the line it belongs to, or -1 if the index is invalid * \param cursorDisplayedAtEndOfPreviousLine true if the cursor is displayed at the end of the previous line that match its index */ @@ -238,12 +243,14 @@ namespace NLGUI bool _Embolden; bool _Oblique; // width of the font in pixel. Just a Hint for tabing format (computed with '_') - uint _FontWidth; + float _FontWidth; // height of the font in pixel. // use getFontHeight - uint _FontHeight; - uint _FontLegHeight; + float _FontHeight; + float _FontLegHeight; float _SpaceWidth; + /// last UI scale used to calculate font size + float _Scale; /// the text color NLMISC::CRGBA _Color; /// the shadow mode @@ -260,6 +267,7 @@ namespace NLGUI sint32 _LineMaxW; /// For single line, true if the text is clamped (ie displayed with "...") bool _SingleLineTextClamped; + ucstring _OverflowText; /// Multiple lines handling bool _MultiLine; @@ -341,8 +349,8 @@ namespace NLGUI // Clear the line & remove text contexts void clear(NL3D::UTextContext &textContext); // Add a new word (and its context) in the line + a number of spaces to append at the end of the line - void addWord(const ucstring &word, uint numSpaces, const CFormatInfo &wordFormat, uint fontWidth, NL3D::UTextContext &textContext); - void addWord(const CWord &word, uint fontWidth); + void addWord(const ucstring &word, uint numSpaces, const CFormatInfo &wordFormat, float fontWidth, NL3D::UTextContext &textContext); + void addWord(const CWord &word, float fontWidth); uint getNumWords() const { return (uint)_Words.size(); } CWord &getWord(uint index) { return _Words[index]; } float getSpaceWidth() const { return _SpaceWidth; } @@ -402,7 +410,7 @@ namespace NLGUI uint _TextSelectionEnd; // First line X coordinate - sint _FirstLineX; + float _FirstLineX; /// Dynamic tooltips std::vector _Tooltips; diff --git a/code/nel/include/nel/gui/widget_manager.h b/code/nel/include/nel/gui/widget_manager.h index 4d96be1b5..68576ce0f 100644 --- a/code/nel/include/nel/gui/widget_manager.h +++ b/code/nel/include/nel/gui/widget_manager.h @@ -49,6 +49,7 @@ namespace NLGUI class CProcedure; class IEditorSelectionWatcher; class IWidgetAdditionWatcher; + class IInterfaceScaleWatcher; /** GUI Widget Manager @@ -530,6 +531,11 @@ namespace NLGUI bool unGroupSelection(); void setMultiSelection( bool b ){ multiSelection = b; } + float getInterfaceScale() const { return _InterfaceScale; } + void notifyInterfaceScaleWatchers(); + void registerInterfaceScaleWatcher(IInterfaceScaleWatcher *watcher); + void unregisterInterfaceScaleWatcher(IInterfaceScaleWatcher *watcher); + bool createNewGUI( const std::string &project, const std::string &window ); private: @@ -615,6 +621,7 @@ namespace NLGUI uint32 _ScreenH; uint32 _ScreenW; + float _InterfaceScale; std::vector< CInterfaceAnim* > activeAnims; @@ -622,6 +629,7 @@ namespace NLGUI std::vector< IOnWidgetsDrawnHandler* > onWidgetsDrawnHandlers; std::vector< IEditorSelectionWatcher* > selectionWatchers; std::vector< IWidgetWatcher* > widgetWatchers; + std::vector< IInterfaceScaleWatcher* > scaleWatchers; std::vector< std::string > editorSelection; bool _GroupSelection; diff --git a/code/nel/src/gui/group_editbox.cpp b/code/nel/src/gui/group_editbox.cpp index 13979f4aa..1a800b23a 100644 --- a/code/nel/src/gui/group_editbox.cpp +++ b/code/nel/src/gui/group_editbox.cpp @@ -702,9 +702,9 @@ namespace NLGUI sint32 maxPos= max(_CursorPos, _SelectCursorPos) + (sint32)_Prompt.length(); // get its position on screen - sint cxMinPos, cyMinPos; - sint cxMaxPos, cyMaxPos; - sint height; + float cxMinPos, cyMinPos; + float cxMaxPos, cyMaxPos; + float height; _ViewText->getCharacterPositionFromIndex(minPos, false, cxMinPos, cyMinPos, height); _ViewText->getCharacterPositionFromIndex(maxPos, false, cxMaxPos, cyMaxPos, height); @@ -755,8 +755,8 @@ namespace NLGUI if (_BlinkState) // is the cursor shown ? { // get its position on screen - sint cx, cy; - sint height; + float cx, cy; + float height; _ViewText->getCharacterPositionFromIndex(_CursorPos + (sint)_Prompt.length(), _CursorAtPreviousLineEnd, cx, cy, height); // display the cursor // get the texture for the cursor @@ -1482,7 +1482,7 @@ namespace NLGUI if (_ViewText->getWReal() > _WReal) { // Check if cursor visible - sint xCursVT, xCurs, yTmp, hTmp; + float xCursVT, xCurs, yTmp, hTmp; // Get the cursor pos from the BL of the viewtext _ViewText->getCharacterPositionFromIndex(_CursorPos+(sint)_Prompt.size(), false, xCursVT, yTmp, hTmp); // Get the cursor pos from the BL of the edit box diff --git a/code/nel/src/gui/view_renderer.cpp b/code/nel/src/gui/view_renderer.cpp index fc641d1cf..66da2800b 100644 --- a/code/nel/src/gui/view_renderer.cpp +++ b/code/nel/src/gui/view_renderer.cpp @@ -96,16 +96,13 @@ namespace NLGUI if(w!=0 && h!=0) { _IsMinimized= false; - _ScreenW = w; - _ScreenH = h; - if(_ScreenW>0) - _OneOverScreenW = 1.0f / (float)_ScreenW; - else - _OneOverScreenW = 1000; - if(_ScreenH>0) - _OneOverScreenH = 1.0f / (float)_ScreenH; - else - _OneOverScreenH = 1000; + if (w != _ScreenW || h != _ScreenH) + { + _ScreenW = w; + _ScreenH = h; + + updateInterfaceScale(); + } } else { @@ -114,14 +111,48 @@ namespace NLGUI } } + void CViewRenderer::updateInterfaceScale() + { + if(_ScreenW>0) + _OneOverScreenW = 1.0f / (float)_ScreenW; + else + _OneOverScreenW = 1000; + if(_ScreenH>0) + _OneOverScreenH = 1.0f / (float)_ScreenH; + else + _OneOverScreenH = 1000; + + _InterfaceScale = _InterfaceUserScale; + if (_InterfaceBaseW > 0 && _InterfaceBaseH > 0) + { + float wRatio = (float)_ScreenW / _InterfaceBaseW; + float rRatio = (float)_ScreenH / _InterfaceBaseH; + _InterfaceScale *= std::min(wRatio, rRatio); + } + + if (_InterfaceScale != 1.0f) + { + _OneOverScreenW *= _InterfaceScale; + _OneOverScreenH *= _InterfaceScale; + + _EffectiveScreenW = sint(_ScreenW / _InterfaceScale); + _EffectiveScreenH = sint(_ScreenH / _InterfaceScale); + } + else + { + _EffectiveScreenW = _ScreenW; + _EffectiveScreenH = _ScreenH; + } + } + /* * getScreenSize : get the screen window size */ void CViewRenderer::getScreenSize (uint32 &w, uint32 &h) { - w = _ScreenW; - h = _ScreenH; + w = _EffectiveScreenW; + h = _EffectiveScreenH; } /* @@ -133,6 +164,20 @@ namespace NLGUI ooh= _OneOverScreenH; } + void CViewRenderer::setInterfaceScale(float scale, sint32 width/*=0*/, sint32 height/*=0*/) + { + // prevent #div/0 + if (sint(scale*100) > 0) + _InterfaceUserScale = scale; + else + _InterfaceUserScale = 1.0f; + + _InterfaceBaseW = width; + _InterfaceBaseH = height; + + updateInterfaceScale(); + } + void CViewRenderer::setup() { _ClipX = _ClipY = 0; @@ -140,8 +185,10 @@ namespace NLGUI _ClipH = 600; _ScreenW = 800; _ScreenH = 600; - _OneOverScreenW= 1.0f / (float)_ScreenW; - _OneOverScreenH= 1.0f / (float)_ScreenH; + _InterfaceScale = 1.0f; + _InterfaceUserScale = 1.0f; + _InterfaceBaseW = 0; + _InterfaceBaseH = 0; _IsMinimized= false; _WFigurTexture= 0; _HFigurTexture= 0; @@ -157,6 +204,9 @@ namespace NLGUI _EmptyLayer[i]= true; } _BlankGlobalTexture = NULL; + _Bilinear = false; + + updateInterfaceScale(); } @@ -499,7 +549,7 @@ namespace NLGUI /* * drawBitmap */ - void CViewRenderer::drawRotFlipBitmap (sint layerId, sint32 x, sint32 y, sint32 width, sint32 height, + void CViewRenderer::drawRotFlipBitmap (sint layerId, float x, float y, float width, float height, uint8 rot, bool flipv, sint32 nTxId, const CRGBA &col) { if (width <= 0 || height <= 0) return; @@ -1283,7 +1333,7 @@ namespace NLGUI _Material.setTexture(0, ite->Texture); // Special Case if _WorldSpaceTransformation and _WorldSpaceScale, enable bilinear - if(_WorldSpaceTransformation && _WorldSpaceScale) + if(_Bilinear || (_WorldSpaceTransformation && _WorldSpaceScale)) ite->Texture->setFilterMode(UTexture::Linear, UTexture::LinearMipMapOff); // draw quads and empty list @@ -1300,7 +1350,7 @@ namespace NLGUI } // Special Case if _WorldSpaceTransformation and _WorldSpaceScale, reset - if(_WorldSpaceTransformation && _WorldSpaceScale) + if(_Bilinear || (_WorldSpaceTransformation && _WorldSpaceScale)) ite->Texture->setFilterMode(UTexture::Nearest, UTexture::NearestMipMapOff); } if (!layer.FilteredAlphaBlendedQuads.empty() || @@ -1942,6 +1992,25 @@ namespace NLGUI void CViewRenderer::drawText (sint layerId, float x, float y, uint wordIndex, float xmin, float ymin, float xmax, float ymax, UTextContext &textContext) { + xmin = xmin * _OneOverScreenW; + ymin = ymin * _OneOverScreenH; + xmax = xmax * _OneOverScreenW; + ymax = ymax * _OneOverScreenH; + + if (_InterfaceScale != 1.0f && _InterfaceScale != 2.0f) + { + // align to screen pixel + x *= _OneOverScreenW * _ScreenW; + y *= _OneOverScreenH * _ScreenH; + x = floorf(x) * 1.f / (float) _ScreenW; + y = floorf(y) * 1.f / (float) _ScreenH; + } + else + { + x = floorf(x) * _OneOverScreenW; + y = floorf(y) * _OneOverScreenH; + } + if (_WorldSpaceTransformation) { textContext.printClipAtUnProjected(*getStringRenderBuffer(layerId), _CameraFrustum, _WorldSpaceMatrix, x, y, _CurrentZ, wordIndex, xmin, ymin, xmax, ymax); diff --git a/code/nel/src/gui/view_text.cpp b/code/nel/src/gui/view_text.cpp index cfc2c3b92..7ac1bf8a4 100644 --- a/code/nel/src/gui/view_text.cpp +++ b/code/nel/src/gui/view_text.cpp @@ -86,6 +86,7 @@ namespace NLGUI _MultiMaxLine = 0; _Index = 0xFFFFFFFF; + _Scale = CWidgetManager::getInstance()->getInterfaceScale(); _FontWidth= 0; _FontHeight = 0; _FontLegHeight = 0; @@ -104,6 +105,7 @@ namespace NLGUI _AutoClamp = false; _ClampRight = true; // clamp on the right of the text + _OverflowText = "..."; _LetterColors = NULL; _Setuped= false; @@ -118,6 +120,8 @@ namespace NLGUI :CViewBase(param) { setupDefault (); + + CWidgetManager::getInstance()->registerInterfaceScaleWatcher(this); } ///constructor @@ -135,11 +139,15 @@ namespace NLGUI _ShadowOutline = ShadowOutline; setText(Text); computeFontSize (); + + CWidgetManager::getInstance()->registerInterfaceScaleWatcher(this); } // *************************************************************************** CViewText::~CViewText() { + CWidgetManager::getInstance()->unregisterInterfaceScaleWatcher(this); + if (_Index != 0xFFFFFFFF) CViewRenderer::getTextContext(_FontName)->erase (_Index); clearLines(); @@ -912,8 +920,9 @@ namespace NLGUI // *************************************************************************** sint CViewText::getCurrentMultiLineMaxW() const { + sint maxw = ceilf(_LineMaxW * _Scale); if(_MultiLineMaxWOnly) - return _LineMaxW; + return maxw; else { sint offset = (sint)_XReal - (sint)_Parent->getXReal(); @@ -978,6 +987,31 @@ namespace NLGUI CViewRenderer &rVR = *CViewRenderer::getInstance(); +#if 0 + //rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal, _WReal, _HReal, 0, false, rVR.getBlankTextureId(), CRGBA(64,64,64,255)); + + // debug text with mouse hover + if(CWidgetManager::getInstance()->getPointer()) + { + // but must check first if mouse is over + sint32 x = CWidgetManager::getInstance()->getPointer()->getX(); + sint32 y = CWidgetManager::getInstance()->getPointer()->getY(); + bool mouseIn; + // use parent clip or self clip? + if(_OverExtendViewTextUseParentRect) + mouseIn= _Parent && _Parent->isIn(x,y); + else + mouseIn= isIn(x,y); + // if the mouse cursor is in the clip area + if(mouseIn) { + rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal, _WReal, 1, 0, false, rVR.getBlankTextureId(), CRGBA(200,200,200,255)); + rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal+_HReal, _WReal, 1, 0, false, rVR.getBlankTextureId(), CRGBA(200,200,200,255)); + rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal, 1, _HReal, 0, false, rVR.getBlankTextureId(), CRGBA(200,200,200,255)); + rVR.drawRotFlipBitmap (_RenderLayer, _XReal+_WReal, _YReal, 1, _HReal, 0, false, rVR.getBlankTextureId(), CRGBA(200,200,200,255)); + } + } +#endif + // *** Out Of Clip? sint32 ClipX, ClipY, ClipW, ClipH; rVR.getClipWindow (ClipX, ClipY, ClipW, ClipH); @@ -999,12 +1033,8 @@ namespace NLGUI } // *** Screen Minimized? - uint32 w, h; - float oow, ooh; - rVR.getScreenSize (w, h); if (rVR.isMinimized()) return; - rVR.getScreenOOSize (oow, ooh); NL3D::UTextContext *TextContext = CViewRenderer::getTextContext(_FontName); @@ -1025,6 +1055,8 @@ namespace NLGUI shcol.A = (uint8)(((sint)shcol.A*((sint)CWidgetManager::getInstance()->getGlobalColorForContent().A+1))>>8); } + float oow, ooh; + rVR.getScreenOOSize (oow, ooh); // *** Draw multiline if ((_MultiLine)&&(_Parent != NULL)) @@ -1036,23 +1068,16 @@ namespace NLGUI TextContext->setShadeOutline (_ShadowOutline); TextContext->setShadeColor (shcol); TextContext->setShadeExtent (_ShadowX*oow, _ShadowY*ooh); - TextContext->setFontSize (_FontSize); + TextContext->setFontSize (_FontSize*_Scale); TextContext->setEmbolden (_Embolden); TextContext->setOblique (_Oblique); - float y = (float)(_YReal) * ooh; // y is expressed in scree, coordinates [0..1] - //y += _LinesInfos[_LinesInfos.size()-1].StringLine / h; - // Y is the base line of the string, so it must be grown up. - y += (float)_FontLegHeight * ooh; - - sint y_line = _YReal+_FontLegHeight-2; + float y = _YReal * _Scale + _FontLegHeight; if (_MultiMinLine > _Lines.size()) { - uint dy = getMultiMinOffsetY(); - y += dy * ooh; - y_line += dy; + y += getMultiMinOffsetY() * _Scale; } // special selection code @@ -1093,7 +1118,7 @@ namespace NLGUI { CLine &currLine = *_Lines[i]; // current x position - float px = (float) (_XReal + ((i==0) ? (sint)_FirstLineX : 0)); + float px = (float) (_XReal * _Scale + ((i==0) ? (sint)_FirstLineX : 0)); // draw each words of the line for(uint k = 0; k < currLine.getNumWords(); ++k) { @@ -1120,25 +1145,25 @@ namespace NLGUI px += firstSpace; // skip tabulation before current word if(currWord.Format.TabX) - px= max(px, (float)(_XReal + currWord.Format.TabX*_FontWidth)); + px= max(px, (float)(_XReal * _Scale + currWord.Format.TabX*_FontWidth)); // draw. We take floorf px to avoid filtering of letters that are not located on a pixel boundary - rVR.drawText (_RenderLayer, floorf(px) * oow, y, currWord.Index, (float)ClipX * oow, (float)ClipY * ooh, - (float)(ClipX+ClipW) * oow, (float)(ClipY+ClipH) * ooh, *TextContext); + float fx = px / _Scale; + float fy = y / _Scale; + rVR.drawText (_RenderLayer, fx, fy, currWord.Index, ClipX, ClipY, ClipX+ClipW, ClipY+ClipH, *TextContext); // Draw a line if (_Underlined) - rVR.drawRotFlipBitmap (_RenderLayer, (sint)floorf(px), y_line, line_width, 1, 0, false, rVR.getBlankTextureId(), col); + rVR.drawRotFlipBitmap (_RenderLayer, fx, fy - _FontLegHeight*0.6f / _Scale, line_width / _Scale, 1.0f / _Scale, 0, false, rVR.getBlankTextureId(), col); if (_StrikeThrough) - rVR.drawRotFlipBitmap (_RenderLayer, (sint)floorf(px), y_line + (_FontHeight / 2), line_width, 1, 0, false, rVR.getBlankTextureId(), col); + rVR.drawRotFlipBitmap (_RenderLayer, fx, fy + _FontHeight*0.2f / _Scale, line_width / _Scale, 1.0f / _Scale, 0, false, rVR.getBlankTextureId(), col); // skip word px += currWord.Info.StringWidth; } // go one line up - y += (_FontHeight + _MultiLineSpace) * ooh; - y_line += _FontHeight+_MultiLineSpace; + y += (_FontHeight + _MultiLineSpace * _Scale); } // reset selection @@ -1164,22 +1189,17 @@ namespace NLGUI TextContext->setShadeOutline (_ShadowOutline); TextContext->setShadeColor (shcol); TextContext->setShadeExtent (_ShadowX*oow, _ShadowY*ooh); - TextContext->setFontSize (_FontSize); + TextContext->setFontSize (_FontSize*_Scale); TextContext->setEmbolden (_Embolden); TextContext->setOblique (_Oblique); - if(_LetterColors!=NULL && !TextContext->isSameLetterColors(_LetterColors, _Index)) { TextContext->setLetterColors(_LetterColors, _Index); } - - float x = (float)(_XReal) * oow; - float y = (float)(_YReal) * ooh; - // Y is the base line of the string, so it must be grown up. - y += (float)_FontLegHeight * ooh; + float y = _YReal * _Scale + _FontLegHeight; // special selection code if(_TextSelection) @@ -1190,15 +1210,14 @@ namespace NLGUI TextContext->setStringColor(_Index, col); // draw - rVR.drawText (_RenderLayer, x, y, _Index, (float)ClipX * oow, (float)ClipY * ooh, - (float)(ClipX+ClipW) * oow, (float)(ClipY+ClipH) * ooh, *TextContext); + rVR.drawText (_RenderLayer, _XReal, y / _Scale, _Index, ClipX, ClipY, ClipX+ClipW, ClipY+ClipH, *TextContext); // Draw a line if (_Underlined) - rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal+_FontLegHeight-2, _WReal, 1, 0, false, rVR.getBlankTextureId(), col); + rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal - _FontLegHeight*0.3f/_Scale, _WReal, 1, 0, false, rVR.getBlankTextureId(), col); if (_StrikeThrough) - rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal+(_FontLegHeight/2), _WReal, 1, 0, false, rVR.getBlankTextureId(), col); + rVR.drawRotFlipBitmap (_RenderLayer, _XReal, _YReal + _FontHeight*0.2f / _Scale, _WReal, 1, 0, false, rVR.getBlankTextureId(), col); // reset selection if(_TextSelection) @@ -1247,7 +1266,6 @@ namespace NLGUI } } } - } // *************************************************************************** @@ -1409,7 +1427,7 @@ namespace NLGUI sint32 value; if(CLuaIHM::popSINT32(ls, value)) { - setLineMaxW(value); + setLineMaxW(ceilf(value / _Scale)); } return 0; } @@ -1445,19 +1463,25 @@ namespace NLGUI // *************************************************************************** uint CViewText::getFontWidth() const { - return _FontWidth; + return _FontWidth / _Scale; } // *************************************************************************** uint CViewText::getFontHeight() const { - return _FontHeight; + return _FontHeight / _Scale; } // *************************************************************************** uint CViewText::getFontLegHeight() const { - return _FontLegHeight; + return _FontLegHeight / _Scale; + } + + // *************************************************************************** + float CViewText::getLineHeight() const + { + return _FontHeight / _Scale + _MultiLineSpace; } // *************************************************************************** @@ -1468,7 +1492,7 @@ namespace NLGUI { // first line is always present even if _Lines is empty uint nbLines = _MultiMinLine - std::max((sint)1, (sint)_Lines.size()); - dy = nbLines * _FontHeight + (nbLines - 1) * _MultiLineSpace; + dy = (nbLines * _FontHeight + (nbLines - 1) * _MultiLineSpace * _Scale) / _Scale; } return dy; } @@ -1500,11 +1524,12 @@ namespace NLGUI ucstring ucCurrentWord; CFormatInfo wordFormat; // line state - float rWidthCurrentLine = 0, rWidthLetter; + float rWidthCurrentLine = 0; bool linePushed= false; // for all the text uint textSize= (uint)_Text.size(); uint formatTagIndex= 0; + nMaxWidth *= _Scale; for (i = 0; i < textSize; ++i) { if(isFormatTagChange(i, formatTagIndex)) @@ -1537,21 +1562,20 @@ namespace NLGUI ucstring ucStrLetter; ucStrLetter= ucLetter; si = TextContext->getStringInfo (ucStrLetter); - rWidthLetter = (si.StringWidth); - if ((rWidthCurrentLine + rWidthLetter) > nMaxWidth) + if ((rWidthCurrentLine + si.StringWidth) > nMaxWidth) { flushWordInLine(ucCurrentWord, linePushed, wordFormat); // reset line state, and begin with the cut letter linePushed= false; - rWidthCurrentLine = rWidthLetter; + rWidthCurrentLine = si.StringWidth; ucCurrentWord = ucLetter; } else { // Grow the current word ucCurrentWord += ucLetter; - rWidthCurrentLine += rWidthLetter; + rWidthCurrentLine += si.StringWidth; } } } @@ -1625,12 +1649,13 @@ namespace NLGUI CFormatInfo wordFormat; uint formatTagIndex= 0; // + nMaxWidth *= _Scale; while (currPos != _Text.length()) { TCharPos spaceEnd; TCharPos wordEnd; uint numSpaces; - float newLineWidth; + float newLineWidth = 0; breakLine = false; // if (_Text[currPos] == (ucchar) '\n') @@ -1903,7 +1928,7 @@ namespace NLGUI TextContext->setHotSpot (UTextContext::BottomLeft); TextContext->setShaded (_Shadow); TextContext->setShadeOutline (_ShadowOutline); - TextContext->setFontSize (_FontSize); + TextContext->setFontSize (_FontSize*_Scale); TextContext->setEmbolden (_Embolden); TextContext->setOblique (_Oblique); @@ -1912,7 +1937,7 @@ namespace NLGUI if ((_MultiLine)&&(_Parent != NULL)) { - sint nMaxWidth = getCurrentMultiLineMaxW(); + float nMaxWidth = getCurrentMultiLineMaxW(); _LastMultiLineMaxW = nMaxWidth; clearLines(); if (nMaxWidth <= 0) @@ -1935,26 +1960,30 @@ namespace NLGUI _Lines.back()->clear(*TextContext); _Lines.pop_back(); } - _Lines.pop_back(); - CViewText::CLine *endLine = new CViewText::CLine; - CViewText::CWord w; - w.build(string("..."), *TextContext); - endLine->addWord(w, _FontWidth); - _Lines.push_back(TLineSPtr(endLine)); + if (_OverflowText.size() > 0) + { + _Lines.pop_back(); + CViewText::CLine *endLine = new CViewText::CLine; + CViewText::CWord w; + w.build(_OverflowText, *TextContext); + endLine->addWord(w, _FontWidth); + _Lines.push_back(TLineSPtr(endLine)); + } } // Calculate size + float rMultiLineSpace = _MultiLineSpace * _Scale; float rTotalW = 0; for (uint i = 0; i < _Lines.size(); ++i) { rTotalW = std::max(_Lines[i]->getWidth() + ((i==0)?_FirstLineX:0), rTotalW); } - _W = (sint)rTotalW; - _H = std::max(_FontHeight, uint(_FontHeight * _Lines.size() + std::max(0, sint(_Lines.size()) - 1) * _MultiLineSpace)); + _W = (sint)ceilf(rTotalW / _Scale); + _H = std::max(_FontHeight / _Scale, ceilf((_FontHeight * _Lines.size() + std::max(0, sint(_Lines.size()) - 1) * rMultiLineSpace) / _Scale)); // See if we should pretend to have at least X lines if (_MultiMinLine > 1) - _H = std::max(_H, sint(_FontHeight * _MultiMinLine + (_MultiMinLine - 1) * _MultiLineSpace)); + _H = std::max(_H, sint((_FontHeight * _MultiMinLine + (_MultiMinLine - 1) * rMultiLineSpace)/_Scale)); // Compute tooltips size if (!_Tooltips.empty()) @@ -1963,7 +1992,6 @@ namespace NLGUI for (uint j=0 ; j<_Lines[i]->getNumWords() ; ++j) { CWord word = _Lines[i]->getWord(j); - // float w = _Lines[i]->getWidth(); if (word.Format.IndexTt != -1) { @@ -1971,7 +1999,7 @@ namespace NLGUI { CCtrlToolTip *pTooltip = _Tooltips[word.Format.IndexTt]; - sint y = (sint) ((_FontHeight + _MultiLineSpace) * (_Lines.size() - i - 1)); + sint y = (sint) ceilf(((_FontHeight + rMultiLineSpace) * (_Lines.size() - i - 1))/_Scale); pTooltip->setX(0); pTooltip->setY(y); @@ -1990,10 +2018,10 @@ namespace NLGUI // Common case: no W clamp _Index = TextContext->textPush (_Text); _Info = TextContext->getStringInfo (_Index); - _W = (sint)(_Info.StringWidth); + _W = (sint)ceilf(_Info.StringWidth / _Scale); // Rare case: clamp W => recompute slowly, cut letters - if(_W>_LineMaxW) + if(_Info.StringWidth > _LineMaxW) { TextContext->erase (_Index); @@ -2001,10 +2029,16 @@ namespace NLGUI UTextContext::CStringInfo si; ucstring ucCurrentLine; ucCurrentLine.reserve(_Text.size()); + // Append ... to the end of line - si = TextContext->getStringInfo (ucstring("...")); - float dotWidth= si.StringWidth; - float rWidthCurrentLine = 0, rWidthLetter; + float dotWidth = 0.f; + if (_OverflowText.size() > 0) + { + si = TextContext->getStringInfo (ucstring(_OverflowText)); + dotWidth = si.StringWidth; + } + + float rWidthCurrentLine = 0; // for all the text if (_ClampRight) { @@ -2014,8 +2048,7 @@ namespace NLGUI ucstring ucStrLetter; ucStrLetter= ucLetter; si = TextContext->getStringInfo (ucStrLetter); - rWidthLetter = (si.StringWidth); - if ((rWidthCurrentLine + rWidthLetter + dotWidth) > _LineMaxW) + if ((rWidthCurrentLine + si.StringWidth + dotWidth) > _LineMaxW) { break; } @@ -2023,12 +2056,15 @@ namespace NLGUI { // Grow the current line ucCurrentLine += ucLetter; - rWidthCurrentLine += rWidthLetter; + rWidthCurrentLine += si.StringWidth; } } // Add the dots - ucCurrentLine+= "..."; + if (_OverflowText.size() > 0) + { + ucCurrentLine += _OverflowText; + } } else { @@ -2038,8 +2074,7 @@ namespace NLGUI ucstring ucStrLetter; ucStrLetter= ucLetter; si = TextContext->getStringInfo (ucStrLetter); - rWidthLetter = (si.StringWidth); - if ((rWidthCurrentLine + rWidthLetter + dotWidth) > _LineMaxW) + if ((rWidthCurrentLine + si.StringWidth + dotWidth) > _LineMaxW) { break; } @@ -2047,24 +2082,27 @@ namespace NLGUI { // Grow the current line ucCurrentLine = ucLetter + ucCurrentLine; - rWidthCurrentLine += rWidthLetter; + rWidthCurrentLine += si.StringWidth; } } // Add the dots - ucCurrentLine = "..." + ucCurrentLine; + if (_OverflowText.size() > 0) + { + ucCurrentLine = _OverflowText + ucCurrentLine; + } } // And so setup this trunc text _Index = TextContext->textPush (ucCurrentLine); _Info = TextContext->getStringInfo (_Index); - _W = (sint)(_Info.StringWidth); + _W = (sint)ceilf(_Info.StringWidth / _Scale); _SingleLineTextClamped= true; } // same height always - _H = _FontHeight; + _H = (sint)ceilf(_FontHeight / _Scale); } _InvalidTextContext= false; @@ -2223,28 +2261,29 @@ namespace NLGUI } // *************************************************************************** - void CViewText::getCharacterPositionFromIndex(sint index, bool cursorAtPreviousLineEnd, sint &x, sint &y, sint &height) const + void CViewText::getCharacterPositionFromIndex(sint index, bool cursorAtPreviousLineEnd, float &x, float &y, float &height) const { NLMISC::clamp(index, 0, (sint) _Text.length()); NL3D::UTextContext *TextContext = CViewRenderer::getTextContext(_FontName); TextContext->setHotSpot (UTextContext::BottomLeft); TextContext->setShaded (_Shadow); TextContext->setShadeOutline (_ShadowOutline); - TextContext->setFontSize (_FontSize); + TextContext->setFontSize (_FontSize*_Scale); TextContext->setEmbolden (_Embolden); TextContext->setOblique (_Oblique); - // CViewRenderer &rVR = *CViewRenderer::getInstance(); - height = getFontHeight(); + height = getLineHeight(); // if (_MultiLine) { - uint dy = getMultiMinOffsetY(); + float fx, fy; + float dy = getMultiMinOffsetY() * _Scale; + float nMaxWidth = getCurrentMultiLineMaxW() * _Scale; uint charIndex = 0; // special case for end of text if (index == (sint) _Text.length()) { - y = dy; + fy = dy; if (_Lines.empty()) { x = 0; @@ -2252,10 +2291,12 @@ namespace NLGUI else { CLine &lastLine = *_Lines.back(); - x = (sint) (lastLine.getWidth() + lastLine.getEndSpaces() * lastLine.getSpaceWidth()); - sint nMaxWidth = getCurrentMultiLineMaxW(); - x = std::min(x, nMaxWidth); + fx = lastLine.getWidth() + lastLine.getEndSpaces() * lastLine.getSpaceWidth(); + fx = std::min(fx, nMaxWidth); } + + x = fx / _Scale; + y = fy / _Scale; return; } for(sint i = 0; i < (sint) _Lines.size(); ++i) @@ -2264,10 +2305,12 @@ namespace NLGUI { // should display the character at the end of previous line CLine &currLine = *_Lines[i - 1]; - y = (sint) ((_FontHeight + _MultiLineSpace) * (_Lines.size() - i) + dy); - x = (sint) (currLine.getWidth() + currLine.getEndSpaces() * currLine.getSpaceWidth()); - sint nMaxWidth = getCurrentMultiLineMaxW(); - x = std::min(x, nMaxWidth); + fy = (_FontHeight + _MultiLineSpace * _Scale) * (_Lines.size() - i) + dy; + fx = currLine.getWidth() + currLine.getEndSpaces() * currLine.getSpaceWidth(); + fx = std::min(fx, nMaxWidth); + + x = fx / _Scale; + y = fy / _Scale; return; } CLine &currLine = *_Lines[i]; @@ -2275,14 +2318,16 @@ namespace NLGUI if ((sint) newCharIndex > index) { // ok, this line contains the character, now, see which word contains it. - y = (sint) ((_FontHeight + _MultiLineSpace) * (_Lines.size() - 1 - i) + dy); + fy = (_FontHeight + _MultiLineSpace * _Scale) * (_Lines.size() - 1 - i) + dy; // see if the index is in the spaces at the end of line if (index - charIndex >= currLine.getNumChars()) { uint numSpaces = index - charIndex - currLine.getNumChars(); - x = (sint) (currLine.getWidth() + numSpaces * _SpaceWidth); - sint nMaxWidth = getCurrentMultiLineMaxW(); - x = std::min(x, nMaxWidth); + fx = currLine.getWidth() + numSpaces * _SpaceWidth; + fx = std::min(fx, nMaxWidth); + + x = fx / _Scale; + y = fy / _Scale; return; } // now, search containing word in current line @@ -2300,15 +2345,19 @@ namespace NLGUI ucstring subStr = currWord.Text.substr(0, index - charIndex - currWord.NumSpaces); // compute the size UTextContext::CStringInfo si = TextContext->getStringInfo(subStr); - x = (sint) (px + si.StringWidth + currWord.NumSpaces * currLine.getSpaceWidth()); - height = getFontHeight(); + fx = px + si.StringWidth + currWord.NumSpaces * currLine.getSpaceWidth(); + + x = fx / _Scale; + y = fy / _Scale; return; } else { // character is in the spaces preceding the word - x = (sint) (px + currLine.getSpaceWidth() * (index - charIndex)); - height = getFontHeight(); + fx = px + currLine.getSpaceWidth() * (index - charIndex); + + x = fx / _Scale; + y = fy / _Scale; return; } } @@ -2327,15 +2376,16 @@ namespace NLGUI // compute the size UTextContext::CStringInfo si = TextContext->getStringInfo(subStr); y = 0; - x = (sint) si.StringWidth; + x = (sint) ceilf(si.StringWidth / _Scale); } } // *************************************************************************** - // Tool fct : From a word and a x coordinate, give the matching character index + // Tool fct : From a word and a x coordinate (font scale), give the matching character index static uint getCharacterIndex(const ucstring &textValue, float x, NL3D::UTextContext &textContext) { float px = 0.f; + UTextContext::CStringInfo si; ucstring singleChar(" "); uint i; @@ -2349,7 +2399,7 @@ namespace NLGUI if (px > x) { // if the half of the character is after the cursor, then prefer select the next one (like in Word) - if(px-si.StringWidth/2 < x) + if(px-si.StringWidth/2.f < x) i++; break; } @@ -2358,15 +2408,18 @@ namespace NLGUI } // *************************************************************************** - void CViewText::getCharacterIndexFromPosition(sint x, sint y, uint &index, bool &cursorAtPreviousLineEnd) const + void CViewText::getCharacterIndexFromPosition(float x, float y, uint &index, bool &cursorAtPreviousLineEnd) const { NL3D::UTextContext *TextContext = CViewRenderer::getTextContext(_FontName); + x *= _Scale; + y = roundf(y * _Scale); + // setup the text context TextContext->setHotSpot (UTextContext::BottomLeft); TextContext->setShaded (_Shadow); TextContext->setShadeOutline (_ShadowOutline); - TextContext->setFontSize (_FontSize); + TextContext->setFontSize (_FontSize*_Scale); TextContext->setEmbolden (_Embolden); TextContext->setOblique (_Oblique); // find the line where the character is @@ -2374,7 +2427,7 @@ namespace NLGUI uint charPos = 0; if (_MultiLine) { - y -= getMultiMinOffsetY(); + y -= getMultiMinOffsetY() * _Scale; // seek the line float py = 0.f; if (py > y) @@ -2386,7 +2439,7 @@ namespace NLGUI sint line; for (line = (uint)_Lines.size() - 1; line >= 0; --line) { - float newPy = py + _FontHeight + _MultiLineSpace; + float newPy = py + _FontHeight + _MultiLineSpace * _Scale; if (newPy > y) { break; @@ -2419,6 +2472,7 @@ namespace NLGUI return; } + cursorAtPreviousLineEnd = false; float px = (float)_FirstLineX; for(uint k = 0; k < currLine.getNumWords(); ++k) { @@ -2435,14 +2489,12 @@ namespace NLGUI : 0; clamp(numSpaces, 0, (sint)currWord.NumSpaces); index = numSpaces + charPos; - cursorAtPreviousLineEnd = false; return; } else { // the coord is in the word itself - index = charPos + currWord.NumSpaces + getCharacterIndex(currWord.Text, (float) x - (px + spacesWidth), *TextContext); - cursorAtPreviousLineEnd = false; + index = charPos + currWord.NumSpaces + getCharacterIndex(currWord.Text, x - (px + spacesWidth), *TextContext); return; } } @@ -2450,7 +2502,6 @@ namespace NLGUI charPos += (uint)currWord.Text.length() + currWord.NumSpaces; } index = charPos; - cursorAtPreviousLineEnd = false; return; } else @@ -2466,7 +2517,7 @@ namespace NLGUI index = 0; return; } - index = getCharacterIndex(_Text, (float) x, *TextContext); + index = getCharacterIndex(_Text, x, *TextContext); return; } } @@ -2534,21 +2585,21 @@ namespace NLGUI // *************************************************************************** uint CViewText::getFirstLineX() const { - return _FirstLineX; + return _FirstLineX / _Scale; } // *************************************************************************** uint CViewText::getLastLineW () const { if (!_Lines.empty()) - return (uint)_Lines.back()->getWidth(); + return (uint)_Lines.back()->getWidth() / _Scale; return 0; } // *************************************************************************** void CViewText::setFirstLineX(sint firstLineX) { - _FirstLineX = firstLineX; + _FirstLineX = firstLineX * _Scale; } ///////////////////////////////////// @@ -2567,7 +2618,7 @@ namespace NLGUI } // *************************************************************************** - void CViewText::CLine::addWord(const ucstring &text, uint numSpaces, const CFormatInfo &wordFormat, uint fontWidth, NL3D::UTextContext &textContext) + void CViewText::CLine::addWord(const ucstring &text, uint numSpaces, const CFormatInfo &wordFormat, float fontWidth, NL3D::UTextContext &textContext) { CWord word; word.build(text, textContext, numSpaces); @@ -2576,7 +2627,7 @@ namespace NLGUI } // *************************************************************************** - void CViewText::CLine::addWord(const CWord &word, uint fontWidth) + void CViewText::CLine::addWord(const CWord &word, float fontWidth) { _Words.push_back(word); _NumChars += word.NumSpaces + uint(word.Text.length()); @@ -2586,7 +2637,7 @@ namespace NLGUI _StringLine = word.Info.StringLine; } // the width of the line must reach at least the Tab - _WidthWithoutSpaces= max(_WidthWithoutSpaces, word.Format.TabX * float(fontWidth)); + _WidthWithoutSpaces= max(_WidthWithoutSpaces, word.Format.TabX * fontWidth); // append the text space _WidthWithoutSpaces += word.Info.StringWidth; } @@ -2644,7 +2695,7 @@ namespace NLGUI TextContext->setHotSpot (UTextContext::BottomLeft); TextContext->setShaded (_Shadow); TextContext->setShadeOutline (_ShadowOutline); - TextContext->setFontSize (_FontSize); + TextContext->setFontSize (_FontSize*_Scale); TextContext->setEmbolden (_Embolden); TextContext->setOblique (_Oblique); @@ -2705,14 +2756,14 @@ namespace NLGUI linePos = lineEnd+1; } - return (sint32)maxWidth; + return (sint32)ceilf(maxWidth / _Scale); } // *************************************************************************** sint32 CViewText::getMinUsedW() const { static const ucstring spaceOrLineFeedStr(" \n\t"); - sint32 maxWidth = 0; + float maxWidth = 0.0f; // Not multi line ? Same size than min if (!_MultiLine) @@ -2722,7 +2773,7 @@ namespace NLGUI if (_TextMode == ClipWord) { // No largest font parameter, return the font height - return _FontHeight; + return (sint32)ceilf(_FontHeight / _Scale); } // If we can't clip the words, return the size of the largest word else if ((_TextMode == DontClipWord) || (_TextMode == Justified)) @@ -2731,7 +2782,7 @@ namespace NLGUI TextContext->setHotSpot (UTextContext::BottomLeft); TextContext->setShaded (_Shadow); TextContext->setShadeOutline (_ShadowOutline); - TextContext->setFontSize (_FontSize); + TextContext->setFontSize (_FontSize*_Scale); TextContext->setEmbolden (_Embolden); TextContext->setOblique (_Oblique); @@ -2759,16 +2810,15 @@ namespace NLGUI si = TextContext->getStringInfo(wordValue); // Larger ? - sint32 stringWidth = (sint32)si.StringWidth; - if (stringWidth>maxWidth) - maxWidth = stringWidth; + if (maxWidth < si.StringWidth) + maxWidth = si.StringWidth; // Next word currPos = wordEnd; } } - return maxWidth; + return ceilf(maxWidth / _Scale); } // *************************************************************************** @@ -2782,6 +2832,17 @@ namespace NLGUI invalidateCoords(); } + // *************************************************************************** + void CViewText::onInterfaceScaleChanged() + { + _Scale = CWidgetManager::getInstance()->getInterfaceScale(); + + computeFontSize (); + invalidateContent(); + + CViewBase::onInterfaceScaleChanged(); + } + // *************************************************************************** void CViewText::computeFontSize () { @@ -2789,7 +2850,7 @@ namespace NLGUI TextContext->setHotSpot (UTextContext::BottomLeft); TextContext->setShaded (_Shadow); TextContext->setShadeOutline (_ShadowOutline); - TextContext->setFontSize (_FontSize); + TextContext->setFontSize (_FontSize * _Scale); TextContext->setEmbolden (_Embolden); TextContext->setOblique (_Oblique); @@ -2811,8 +2872,8 @@ namespace NLGUI si = TextContext->getStringInfo(chars); } // add a padding of 1 pixel else the top will be truncated - _FontHeight = (uint) si.StringHeight+1; - _FontLegHeight = (uint) si.StringLine; + _FontHeight = si.StringHeight + 1; + _FontLegHeight = si.StringLine; // Space width si = TextContext->getStringInfo(ucstring(" ")); @@ -2820,7 +2881,7 @@ namespace NLGUI // Font Width si = TextContext->getStringInfo(ucstring("_")); - _FontWidth = (uint)si.StringWidth; + _FontWidth = si.StringWidth; } diff --git a/code/nel/src/gui/widget_manager.cpp b/code/nel/src/gui/widget_manager.cpp index d256704cb..e1cd14ab0 100644 --- a/code/nel/src/gui/widget_manager.cpp +++ b/code/nel/src/gui/widget_manager.cpp @@ -1850,7 +1850,7 @@ namespace NLGUI class InvalidateTextVisitor : public CInterfaceElementVisitor { public: - InvalidateTextVisitor( bool reset ) + InvalidateTextVisitor( bool reset) { this->reset = reset; } @@ -1893,6 +1893,13 @@ namespace NLGUI } CViewRenderer::getInstance()->setClipWindow(0, 0, w, h); + bool scaleChanged = _InterfaceScale != CViewRenderer::getInstance()->getInterfaceScale(); + if (scaleChanged) + { + _InterfaceScale = CViewRenderer::getInstance()->getInterfaceScale(); + notifyInterfaceScaleWatchers(); + } + // If all conditions are OK, move windows so they fit correctly with new screen size // Do this work only InGame when Config is loaded moveAllWindowsToNewScreenSize(w,h,true); @@ -1902,7 +1909,7 @@ namespace NLGUI { SMasterGroup &rMG = _MasterGroups[nMasterGroup]; - InvalidateTextVisitor inv( false ); + InvalidateTextVisitor inv( false); rMG.Group->visitGroupAndChildren( &inv ); rMG.Group->invalidateCoords (); @@ -3688,6 +3695,42 @@ namespace NLGUI } + // ------------------------------------------------------------------------------------------------ + void CWidgetManager::notifyInterfaceScaleWatchers() + { + std::vector< IInterfaceScaleWatcher* >::iterator itr = scaleWatchers.begin(); + while( itr != scaleWatchers.end() ) + { + (*itr)->onInterfaceScaleChanged(); + ++itr; + } + } + + // ------------------------------------------------------------------------------------------------ + void CWidgetManager::registerInterfaceScaleWatcher( IInterfaceScaleWatcher *watcher ) + { + std::vector< IInterfaceScaleWatcher* >::const_iterator itr + = std::find( scaleWatchers.begin(), scaleWatchers.end(), watcher ); + + if( itr != scaleWatchers.end() ) + return; + + scaleWatchers.push_back( watcher ); + } + + // ------------------------------------------------------------------------------------------------ + void CWidgetManager::unregisterInterfaceScaleWatcher( IInterfaceScaleWatcher *watcher ) + { + std::vector< IInterfaceScaleWatcher* >::iterator itr + = std::find( scaleWatchers.begin(), scaleWatchers.end(), watcher ); + + if( itr == scaleWatchers.end() ) + return; + + scaleWatchers.erase( itr ); + } + + // ------------------------------------------------------------------------------------------------ CWidgetManager::CWidgetManager() { LinkHack(); @@ -3724,6 +3767,7 @@ namespace NLGUI inGame = false; setScreenWH(0, 0); + _InterfaceScale = 1.0f; _GroupSelection = false; multiSelection = false; diff --git a/code/nel/tools/3d/build_interface/main.cpp b/code/nel/tools/3d/build_interface/main.cpp index a8f854721..58c97e513 100644 --- a/code/nel/tools/3d/build_interface/main.cpp +++ b/code/nel/tools/3d/build_interface/main.cpp @@ -214,6 +214,7 @@ int main(int argc, char **argv) args.addArg("x", "extract", "", "Extract all interface elements from to ."); args.addAdditionalArg("output_filename", "PNG or TGA file to generate", true); args.addAdditionalArg("input_path", "Path that containts interfaces elements", false); + args.addArg("b", "border", "", "Duplicate icon border to allow bilinear filtering"); if (!args.parse(argc, argv)) return 1; @@ -227,6 +228,13 @@ int main(int argc, char **argv) existingUVfilename = args.getArg("s").front(); } + // + uint borderSize = 0; + if (args.haveArg("b")) + { + borderSize = 1; + } + // extract all interface elements bool extractElements = args.haveArg("x"); @@ -407,6 +415,18 @@ int main(int argc, char **argv) pBtmp->convertToType(CBitmap::RGBA); } + // duplicate icon border + if (borderSize > 0) + { + NLMISC::CBitmap *tmp = new NLMISC::CBitmap; + tmp->resize(pBtmp->getWidth(), pBtmp->getHeight()); + tmp->blit(pBtmp, 0, 0); + tmp->resample(tmp->getWidth() + borderSize * 2, tmp->getHeight() + borderSize * 2); + tmp->blit(pBtmp, borderSize, borderSize); + delete pBtmp; + pBtmp = tmp; + } + AllMaps[i] = pBtmp; } catch (const NLMISC::Exception &e) @@ -461,10 +481,10 @@ int main(int argc, char **argv) putIn (AllMaps[i], &GlobalTexture, x, y); putIn (AllMaps[i], &GlobalMask, x, y, false); - UVMin[i].U = (float)x; - UVMin[i].V = (float)y; - UVMax[i].U = (float)x+AllMaps[i]->getWidth(); - UVMax[i].V = (float)y+AllMaps[i]->getHeight(); + UVMin[i].U = (float)x + borderSize; + UVMin[i].V = (float)y + borderSize; + UVMax[i].U = (float)x + AllMaps[i]->getWidth() - borderSize; + UVMax[i].V = (float)y + AllMaps[i]->getHeight() - borderSize; #if 0 // Do not remove this is useful for debugging diff --git a/code/ryzom/client/data/gamedev/interfaces_v3/commands.xml b/code/ryzom/client/data/gamedev/interfaces_v3/commands.xml index 2a8897555..13356083c 100644 --- a/code/ryzom/client/data/gamedev/interfaces_v3/commands.xml +++ b/code/ryzom/client/data/gamedev/interfaces_v3/commands.xml @@ -122,6 +122,8 @@ + + diff --git a/code/ryzom/client/src/client_cfg.cpp b/code/ryzom/client/src/client_cfg.cpp index 112875cc9..e808361fe 100644 --- a/code/ryzom/client/src/client_cfg.cpp +++ b/code/ryzom/client/src/client_cfg.cpp @@ -300,6 +300,9 @@ CClientConfig::CClientConfig() Luminosity = 0.f; // Default Monitor Luminosity. Gamma = 0.f; // Default Monitor Gamma. + InterfaceScale = 1.0f; // Resize UI + BilinearUI = false; + VREnable = false; VRDisplayDevice = "Auto"; VRDisplayDeviceId = ""; @@ -838,6 +841,10 @@ void CClientConfig::setValues() READ_FLOAT_FV(Luminosity) // Gamma READ_FLOAT_FV(Gamma) + // UI scaling + READ_FLOAT_FV(InterfaceScale); + clamp(ClientCfg.InterfaceScale, MIN_INTERFACE_SCALE, MAX_INTERFACE_SCALE); + READ_BOOL_FV(BilinearUI); // 3D Driver varPtr = ClientCfg.ConfigFile.getVarPtr ("Driver3D"); if (varPtr) diff --git a/code/ryzom/client/src/client_cfg.h b/code/ryzom/client/src/client_cfg.h index f562dfde0..c9ecebd2d 100644 --- a/code/ryzom/client/src/client_cfg.h +++ b/code/ryzom/client/src/client_cfg.h @@ -46,6 +46,9 @@ using NLMISC::CVector; using NLMISC::CRGBA; using std::string; +// limits for UI scale +const float MIN_INTERFACE_SCALE = 0.8; +const float MAX_INTERFACE_SCALE = 2.0; //--------------------------------------------------- // CClientConfig : @@ -146,6 +149,10 @@ struct CClientConfig /// Monitor Gamma [-1 ~ 1], default 0 float Gamma; + // UI scaling + float InterfaceScale; + bool BilinearUI; + // VR bool VREnable; std::string VRDisplayDevice; diff --git a/code/ryzom/client/src/connection.cpp b/code/ryzom/client/src/connection.cpp index ef1a491ab..045822370 100644 --- a/code/ryzom/client/src/connection.cpp +++ b/code/ryzom/client/src/connection.cpp @@ -227,6 +227,9 @@ void connectionRestoreVideoMode () SetMouseCursor (); SetMouseSpeed (ClientCfg.CursorSpeed); SetMouseAcceleration (ClientCfg.CursorAcceleration); + + // Restore user UI scaling + CViewRenderer::getInstance()->setInterfaceScale(ClientCfg.InterfaceScale); } @@ -262,6 +265,8 @@ void setOutGameFullScreen() InitMouseWithCursor(ClientCfg.HardwareCursor && !StereoDisplayAttached); } + // Enable auto scaling in login window + CViewRenderer::getInstance()->setInterfaceScale(1.0f, 1024, 768); } @@ -427,6 +432,9 @@ bool connection (const string &cookie, const string &fsaddr) firstConnection = false; + // Restore user UI scaling + CViewRenderer::getInstance()->setInterfaceScale(ClientCfg.InterfaceScale); + // Disable inputs Actions.enable(false); EditActions.enable(false); @@ -558,6 +566,9 @@ bool reconnection() InterfaceState = globalMenu(); } + // Restore user UI scaling + CViewRenderer::getInstance()->setInterfaceScale(ClientCfg.InterfaceScale); + // Disable inputs Actions.enable(false); EditActions.enable(false); diff --git a/code/ryzom/client/src/init.cpp b/code/ryzom/client/src/init.cpp index 6fc794d52..b653127f4 100644 --- a/code/ryzom/client/src/init.cpp +++ b/code/ryzom/client/src/init.cpp @@ -1332,6 +1332,8 @@ void prelogInit() CInterfaceManager::getInstance(); + CViewRenderer::getInstance()->setInterfaceScale(1.0f, 1024, 768); + CViewRenderer::getInstance()->setBilinearFiltering(ClientCfg.BilinearUI); // Yoyo: initialize NOW the InputHandler for Event filtering. CInputHandlerManager *InputHandlerManager = CInputHandlerManager::getInstance(); diff --git a/code/ryzom/client/src/input.cpp b/code/ryzom/client/src/input.cpp index df4b15c61..bf90d92bd 100644 --- a/code/ryzom/client/src/input.cpp +++ b/code/ryzom/client/src/input.cpp @@ -179,9 +179,9 @@ void SetMouseCursor (bool updatePos) // Get the last cursor float x = 0.5f, y = 0.5f; - // Window size - uint width = Driver->getWindowWidth(); - uint height = Driver->getWindowHeight(); + // Screen size + uint width, height; + CViewRenderer::getInstance()->getScreenSize(width, height); // Update the interface pointer CInterfaceManager *instance = CInterfaceManager::getInstance(); diff --git a/code/ryzom/client/src/interface_v3/action_handler_edit.cpp b/code/ryzom/client/src/interface_v3/action_handler_edit.cpp index 2b3bb7c5e..9c44fa492 100644 --- a/code/ryzom/client/src/interface_v3/action_handler_edit.cpp +++ b/code/ryzom/client/src/interface_v3/action_handler_edit.cpp @@ -383,10 +383,10 @@ class CAHEditPreviousLine : public CAHEdit // .. so do nothing return; } - sint cx, cy; - sint height; + float cx, cy; + float height; _GroupEdit->getViewText()->getCharacterPositionFromIndex(cursorPosInText, _GroupEdit->isCursorAtPreviousLineEnd(), cx, cy, height); - cy += height + _GroupEdit->getViewText()->getMultiLineSpace(); + cy += _GroupEdit->getViewText()->getLineHeight(); uint newCharIndex; bool newLineEnd; _GroupEdit->getViewText()->getCharacterIndexFromPosition(cx, cy, newCharIndex, newLineEnd); @@ -401,8 +401,8 @@ class CAHEditPreviousLine : public CAHEdit } _GroupEdit->setCursorAtPreviousLineEnd(false); // takes character whose X is closer to the current X - sint cx0, cx1; - sint cy0, cy1; + float cx0, cx1; + float cy0, cy1; _GroupEdit->getViewText()->getCharacterPositionFromIndex(newCharIndex, _GroupEdit->isCursorAtPreviousLineEnd(), cx0, cy0, height); _GroupEdit->getViewText()->getCharacterPositionFromIndex(newCharIndex + 1, _GroupEdit->isCursorAtPreviousLineEnd(), cx1, cy1, height); if (abs(cx0 - cx) < abs(cx1 - cx) || cy0 != cy1) @@ -446,8 +446,8 @@ class CAHEditNextLine : public CAHEdit } else if (_GroupEdit->getViewText()->getMultiLine()) { - sint cx, cy; - sint height; + float cx, cy; + float height; _GroupEdit->getViewText()->getCharacterPositionFromIndex(_GroupEdit->getCursorPos() + (sint)_GroupEdit->getPrompt().length(), _GroupEdit->isCursorAtPreviousLineEnd(), cx, cy, height); if (cy != 0) { @@ -466,8 +466,8 @@ class CAHEditNextLine : public CAHEdit } _GroupEdit->setCursorAtPreviousLineEnd(false); // takes character whose X is closer to the current X - sint cx0, cx1; - sint cy0, cy1; + float cx0, cx1; + float cy0, cy1; _GroupEdit->getViewText()->getCharacterPositionFromIndex(newCharIndex, _GroupEdit->isCursorAtPreviousLineEnd(), cx0, cy0, height); _GroupEdit->getViewText()->getCharacterPositionFromIndex(newCharIndex + 1, _GroupEdit->isCursorAtPreviousLineEnd(), cx1, cy1, height); if (abs(cx0 - cx) < abs(cx1 - cx) || cy0 != cy1) diff --git a/code/ryzom/client/src/interface_v3/action_handler_game.cpp b/code/ryzom/client/src/interface_v3/action_handler_game.cpp index 196b0a45e..f1b27bb46 100644 --- a/code/ryzom/client/src/interface_v3/action_handler_game.cpp +++ b/code/ryzom/client/src/interface_v3/action_handler_game.cpp @@ -3732,6 +3732,34 @@ class CHandlerGameConfigChangeScreenRatioCustom : public IActionHandler }; REGISTER_ACTION_HANDLER (CHandlerGameConfigChangeScreenRatioCustom, "game_config_change_screen_ratio_custom"); +// *************************************************************************** +class CHandlerSetInterfaceScale : public IActionHandler +{ + virtual void execute (CCtrlBase *pCaller, const string &Params) + { + std::string s; + s = getParam(Params, "scale"); + if (!s.empty()) { + float scale; + if (fromString(s, scale)) + { + if (scale >= MIN_INTERFACE_SCALE && scale <= MAX_INTERFACE_SCALE) + { + ClientCfg.InterfaceScale = scale; + ClientCfg.writeDouble("InterfaceScale", ClientCfg.InterfaceScale); + + ClientCfg.IsInvalidated = true; + return; + } + } + } + + ucstring help("/setuiscale "+toString("%.1f .. %.1f", MIN_INTERFACE_SCALE, MAX_INTERFACE_SCALE)); + CInterfaceManager::getInstance()->displaySystemInfo(help); + } +}; +REGISTER_ACTION_HANDLER (CHandlerSetInterfaceScale, "set_ui_scale"); + // *************************************************************************** class CHandlerGameMissionAbandon : public IActionHandler diff --git a/code/ryzom/client/src/interface_v3/group_in_scene.cpp b/code/ryzom/client/src/interface_v3/group_in_scene.cpp index fccf2e9e6..b547f7fb0 100644 --- a/code/ryzom/client/src/interface_v3/group_in_scene.cpp +++ b/code/ryzom/client/src/interface_v3/group_in_scene.cpp @@ -86,8 +86,10 @@ void CGroupInScene::computeWindowPos(sint32 &newX, sint32 &newY, CVector &newPro tmp = pVR.getFrustum().projectZ (tmp); // Get the width and height - tmp.x *= (float)CViewRenderer::getInstance()->getDriver()->getWindowWidth(); - tmp.y *= (float)CViewRenderer::getInstance()->getDriver()->getWindowHeight(); + uint32 width, height; + CViewRenderer::getInstance()->getScreenSize(width, height); + tmp.x *= width; + tmp.y *= height; // position without offset, in float newProjCenter.x= tmp.x; diff --git a/code/ryzom/client/src/interface_v3/group_in_scene_bubble.cpp b/code/ryzom/client/src/interface_v3/group_in_scene_bubble.cpp index 5660ef212..063b56131 100644 --- a/code/ryzom/client/src/interface_v3/group_in_scene_bubble.cpp +++ b/code/ryzom/client/src/interface_v3/group_in_scene_bubble.cpp @@ -680,9 +680,8 @@ CGroupInSceneBubbleManager::CPopupContext *CGroupInSceneBubbleManager::buildCont if (target) { // Find a position - NL3D::UDriver *Driver = CViewRenderer::getInstance()->getDriver(); - const uint width = Driver->getWindowWidth(); - const uint height = Driver->getWindowHeight(); + uint32 width, height; + CViewRenderer::getInstance()->getScreenSize(width, height); h = (target->getXReal() < ((sint)width-target->getXReal()-target->getWReal()))?"l":"r"; v = (target->getYReal() < ((sint)height-target->getYReal()-target->getHReal()))?"b":"t"; target->setActive(true); diff --git a/code/ryzom/client/src/interface_v3/interface_manager.cpp b/code/ryzom/client/src/interface_v3/interface_manager.cpp index debe0b9ab..7684accd8 100644 --- a/code/ryzom/client/src/interface_v3/interface_manager.cpp +++ b/code/ryzom/client/src/interface_v3/interface_manager.cpp @@ -484,6 +484,8 @@ CInterfaceManager::CInterfaceManager() interfaceLinkUpdater = new CInterfaceLink::CInterfaceLinkUpdater(); _ScreenW = _ScreenH = 0; _LastInGameScreenW = _LastInGameScreenH = 0; + _InterfaceScaleChanged = false; + _InterfaceScale = 1.0f; _DescTextTarget = NULL; _ConfigLoaded = false; _LogState = false; @@ -1828,8 +1830,12 @@ bool CInterfaceManager::loadConfig (const string &filename) CWidgetManager::getInstance()->setScreenWH(_LastInGameScreenW, _LastInGameScreenH); // NB: we are typically InGame here (even though the _InGame flag is not yet set) // Use the screen size of the config file. Don't update current UI, just _Modes - CWidgetManager::getInstance()->moveAllWindowsToNewScreenSize(ClientCfg.Width, ClientCfg.Height, false); - updateDesktops( ClientCfg.Width, ClientCfg.Height ); + // + // ClientCfg has W/H set to screen size, but interface expects scaled size + sint32 scaledW = ClientCfg.Width / ClientCfg.InterfaceScale; + sint32 scaledH = ClientCfg.Height / ClientCfg.InterfaceScale; + CWidgetManager::getInstance()->moveAllWindowsToNewScreenSize(scaledW, scaledH, false); + updateDesktops(scaledW, scaledH); } // *** apply the current mode @@ -2050,6 +2056,13 @@ void CInterfaceManager::drawViews(NL3D::UCamera camera) _CurrentPlayerCharac[i] = node ? node->getValue32() : 0; } + // scale must be updated right before widget manager checks it + if (_InterfaceScaleChanged) + { + CViewRenderer::getInstance()->setInterfaceScale(_InterfaceScale); + _InterfaceScaleChanged = false; + } + CWidgetManager::getInstance()->drawViews( camera ); // flush obs @@ -2938,7 +2951,6 @@ NLMISC_COMMAND(loadui, "Load an interface file", "") return result; } - // *************************************************************************** void CInterfaceManager::displayWebWindow(const string & name, const string & url) { diff --git a/code/ryzom/client/src/interface_v3/interface_manager.h b/code/ryzom/client/src/interface_v3/interface_manager.h index e83192a3d..652d692b3 100644 --- a/code/ryzom/client/src/interface_v3/interface_manager.h +++ b/code/ryzom/client/src/interface_v3/interface_manager.h @@ -549,6 +549,7 @@ public: NLMISC::CCDBNodeLeaf *_DB_UI_DUMMY_FACTION_TYPE; void updateDesktops( uint32 newScreenW, uint32 newScreenH ); + void setInterfaceScale( float scale ) { _InterfaceScaleChanged = true; _InterfaceScale = scale; } private: @@ -587,6 +588,8 @@ private: uint32 _ScreenW, _ScreenH; // Change res detection sint32 _LastInGameScreenW, _LastInGameScreenH; // Resolution used for last InGame interface + float _InterfaceScale; + bool _InterfaceScaleChanged; // Modes std::vector _Modes; diff --git a/code/ryzom/client/src/main_loop_utilities.cpp b/code/ryzom/client/src/main_loop_utilities.cpp index 415764bfa..37bfc3247 100644 --- a/code/ryzom/client/src/main_loop_utilities.cpp +++ b/code/ryzom/client/src/main_loop_utilities.cpp @@ -36,6 +36,7 @@ #include "input.h" #include "sound_manager.h" #include "camera.h" +#include "interface_v3/interface_manager.h" using namespace NLMISC; using namespace NL3D; @@ -100,6 +101,12 @@ void updateFromClientCfg() Driver->forceTextureResize(1); } + if (ClientCfg.InterfaceScale != LastClientCfg.InterfaceScale) + CInterfaceManager::getInstance()->setInterfaceScale(ClientCfg.InterfaceScale); + + if (ClientCfg.BilinearUI != LastClientCfg.BilinearUI) + CViewRenderer::getInstance()->setBilinearFiltering(ClientCfg.BilinearUI); + //--------------------------------------------------- if (ClientCfg.WaitVBL != LastClientCfg.WaitVBL) { @@ -395,4 +402,4 @@ void updateFromClientCfg() LastClientCfg = ClientCfg; } -/* end of file */ \ No newline at end of file +/* end of file */