Skip to content

Commit b148ff1

Browse files
authoredMar 29, 2023
Merge pull request #52420 from YoannQDQ/map-tip-preview
Map tip preview
2 parents 715f043 + 85a615f commit b148ff1

11 files changed

+481
-92
lines changed
 

‎python/gui/auto_generated/qgsmaptip.sip.in

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,22 @@ Clear the current maptip if it exists
6767
void applyFontSettings();
6868
%Docstring
6969
Apply font family and size to match user settings
70+
%End
71+
72+
static QString vectorMapTipPreviewText( QgsMapLayer *layer, QgsMapCanvas *mapCanvas, const QString &mapTemplate, const QString &displayExpression );
73+
%Docstring
74+
Returns the html that would be displayed in a maptip for a given layer. If the layer has features, the first feature is used
75+
to evaluate the expressions.
76+
77+
.. versionadded:: 3.32
78+
%End
79+
80+
static QString rasterMapTipPreviewText( QgsMapLayer *layer, QgsMapCanvas *mapCanvas, const QString &mapTemplate );
81+
%Docstring
82+
Returns the html that would be displayed in a maptip for a given layer. The center pixel of the raster is used to
83+
evaluate the expressions.
84+
85+
.. versionadded:: 3.32
7086
%End
7187

7288
};

‎python/gui/auto_generated/raster/qgsrasterlayerproperties.sip.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ Adds a properties page factory to the raster layer properties dialog.
4646
virtual QgsExpressionContext createExpressionContext() const;
4747

4848

49+
virtual bool eventFilter( QObject *obj, QEvent *ev );
50+
51+
4952
protected slots:
5053

5154
};

‎python/gui/auto_generated/vector/qgsvectorlayerproperties.sip.in

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ class QgsVectorLayerProperties : QgsOptionsDialogBase
2727
Adds a properties page factory to the vector layer properties dialog.
2828
%End
2929

30+
virtual bool eventFilter( QObject *obj, QEvent *ev );
31+
32+
3033
protected slots:
3134
void optionsStackedWidget_CurrentChanged( int index ) final;
3235

‎src/gui/qgsmaptip.cpp

Lines changed: 128 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -75,11 +75,12 @@ void QgsMapTip::showMapTip( QgsMapLayer *pLayer,
7575

7676
// Do not render a new map tip when the mouse hovers an existing one
7777
if ( mWidget && mWidget->underMouse() )
78+
{
7879
return;
80+
}
7981

8082
// Show the maptip on the canvas
81-
QString tipText, lastTipText, tipHtml, bodyStyle, containerStyle,
82-
backgroundColor, strokeColor, textColor;
83+
QString tipText, lastTipText, tipHtml, backgroundColor, strokeColor;
8384

8485
if ( ! mWidget )
8586
{
@@ -119,9 +120,8 @@ void QgsMapTip::showMapTip( QgsMapLayer *pLayer,
119120
// The content will automatically make it grow up to MaximumSize
120121
mWidget->resize( 0, 0 );
121122

122-
backgroundColor = mWidget->palette().base().color().name();
123-
strokeColor = mWidget->palette().shadow().color().name();
124-
textColor = mWidget->palette().text().color().name();
123+
backgroundColor = QgsApplication::palette().base().color().name();
124+
strokeColor = QgsApplication::palette().shadow().color().name();
125125
mWidget->setStyleSheet( QString(
126126
".QWidget{"
127127
"border: 1px solid %1;"
@@ -154,22 +154,7 @@ void QgsMapTip::showMapTip( QgsMapLayer *pLayer,
154154
return;
155155
}
156156

157-
bodyStyle = QString(
158-
"background-color: %1;"
159-
"margin: 0;"
160-
"font: %2pt \"%3\";"
161-
"color: %4;" ).arg( backgroundColor ).arg( mFontSize ).arg( mFontFamily, textColor );
162-
163-
containerStyle = QString(
164-
"display: inline-block;"
165-
"margin: 0px" );
166-
167-
tipHtml = QString(
168-
"<html>"
169-
"<body style='%1'>"
170-
"<div id='QgsWebViewContainer' style='%2'>%3</div>"
171-
"</body>"
172-
"</html>" ).arg( bodyStyle, containerStyle, tipText );
157+
tipHtml = QgsMapTip::htmlText( tipText );
173158

174159
QgsDebugMsg( tipHtml );
175160

@@ -209,11 +194,15 @@ void QgsMapTip::resizeContent()
209194
void QgsMapTip::clear( QgsMapCanvas *, int msDelay )
210195
{
211196
if ( !mMapTipVisible )
197+
{
212198
return;
199+
}
213200

214201
// Skip clearing the map tip if the user interacts with it or the timer still runs
215202
if ( mDelayedClearTimer.isActive() || mWidget->underMouse() )
203+
{
216204
return;
205+
}
217206

218207
if ( msDelay > 0 )
219208
{
@@ -231,7 +220,9 @@ QString QgsMapTip::fetchFeature( QgsMapLayer *layer, QgsPointXY &mapPosition, Qg
231220
{
232221
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
233222
if ( !vlayer || !vlayer->isSpatial() )
223+
{
234224
return QString();
225+
}
235226

236227
if ( !layer->isInScaleRange( mapCanvas->mapSettings().scale() ) ||
237228
( mapCanvas->mapSettings().isTemporal() && !layer->temporalProperties()->isVisibleInTemporalRange( mapCanvas->temporalRange() ) ) )
@@ -255,7 +246,9 @@ QString QgsMapTip::fetchFeature( QgsMapLayer *layer, QgsPointXY &mapPosition, Qg
255246

256247
const QString canvasFilter = QgsMapCanvasUtils::filterForLayer( mapCanvas, vlayer );
257248
if ( canvasFilter == QLatin1String( "FALSE" ) )
249+
{
258250
return QString();
251+
}
259252

260253
const QString mapTip = vlayer->mapTipTemplate();
261254
QString tipString;
@@ -266,7 +259,9 @@ QString QgsMapTip::fetchFeature( QgsMapLayer *layer, QgsPointXY &mapPosition, Qg
266259
request.setFilterRect( r );
267260
request.setFlags( QgsFeatureRequest::ExactIntersect );
268261
if ( !canvasFilter.isEmpty() )
262+
{
269263
request.setFilterExpression( canvasFilter );
264+
}
270265

271266
if ( mapTip.isEmpty() )
272267
{
@@ -323,7 +318,9 @@ QString QgsMapTip::fetchFeature( QgsMapLayer *layer, QgsPointXY &mapPosition, Qg
323318
}
324319

325320
if ( renderer )
321+
{
326322
renderer->stopRender( renderCtx );
323+
}
327324

328325
return tipString;
329326
}
@@ -332,32 +329,62 @@ QString QgsMapTip::fetchRaster( QgsMapLayer *layer, QgsPointXY &mapPosition, Qgs
332329
{
333330
QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layer );
334331
if ( !rlayer )
332+
{
335333
return QString();
334+
}
336335

337336
if ( !layer->isInScaleRange( mapCanvas->mapSettings().scale() ) ||
338337
( mapCanvas->mapSettings().isTemporal() && !layer->temporalProperties()->isVisibleInTemporalRange( mapCanvas->temporalRange() ) ) )
339338
{
340339
return QString();
341340
}
342341

342+
if ( rlayer->mapTipTemplate().isEmpty() )
343+
{
344+
return QString();
345+
}
346+
343347
const QgsPointXY mappedPosition { mapCanvas->mapSettings().mapToLayerCoordinates( layer, mapPosition ) };
344348

345349
if ( ! layer->extent().contains( mappedPosition ) )
346350
{
347-
return QString( );
351+
return QString();
348352
}
349353

350-
QString tipText { rlayer->mapTipTemplate() };
354+
QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
355+
context.appendScope( QgsExpressionContextUtils::mapSettingsScope( mapCanvas->mapSettings() ) );
356+
context.appendScope( QgsExpressionContextUtils::mapLayerPositionScope( mappedPosition ) );
357+
return QgsExpression::replaceExpressionText( rlayer->mapTipTemplate(), &context );
358+
}
351359

352-
if ( ! tipText.isEmpty() )
353-
{
354-
QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
355-
context.appendScope( QgsExpressionContextUtils::mapSettingsScope( mapCanvas->mapSettings() ) );
356-
context.appendScope( QgsExpressionContextUtils::mapLayerPositionScope( mappedPosition ) );
357-
tipText = QgsExpression::replaceExpressionText( tipText, &context );
358-
}
360+
QString QgsMapTip::htmlText( const QString &text )
361+
{
362+
const QgsSettings settings;
363+
const QFont defaultFont = qApp->font();
364+
const int fontSize = settings.value( QStringLiteral( "/qgis/stylesheet/fontPointSize" ), defaultFont.pointSize() ).toInt();
365+
const QString fontFamily = settings.value( QStringLiteral( "/qgis/stylesheet/fontFamily" ), defaultFont.family() ).toString();
366+
QString bodyStyle, containerStyle, backgroundColor, strokeColor, textColor;
359367

360-
return tipText;
368+
backgroundColor = QgsApplication::palette().base().color().name();
369+
strokeColor = QgsApplication::palette().shadow().color().name();
370+
textColor = QgsApplication::palette().windowText().color().name();
371+
372+
bodyStyle = QString(
373+
"background-color: %1;"
374+
"margin: 0;"
375+
"font: %2pt \"%3\";"
376+
"color: %4;" ).arg( backgroundColor ).arg( fontSize ).arg( fontFamily, textColor );
377+
378+
containerStyle = QString(
379+
"display: inline-block;"
380+
"margin: 0px" );
381+
382+
return QString(
383+
"<html>"
384+
"<body style='%1'>"
385+
"<div id='QgsWebViewContainer' style='%2'>%3</div>"
386+
"</body>"
387+
"</html>" ).arg( bodyStyle, containerStyle, text );
361388
}
362389

363390
void QgsMapTip::applyFontSettings()
@@ -373,3 +400,73 @@ void QgsMapTip::onLinkClicked( const QUrl &url )
373400
{
374401
QDesktopServices::openUrl( url );
375402
}
403+
404+
405+
QString QgsMapTip::vectorMapTipPreviewText( QgsMapLayer *layer, QgsMapCanvas *mapCanvas, const QString &mapTemplate, const QString &displayExpression )
406+
{
407+
// Only spatial layers can have map tips
408+
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
409+
if ( !mapCanvas || !vlayer || !vlayer->isSpatial() )
410+
return QString();
411+
412+
// If no map tip template or display expression is set, return an empty string
413+
if ( mapTemplate.isEmpty() && displayExpression.isEmpty() )
414+
return QString();
415+
416+
// Create an expression context
417+
QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( vlayer ) );
418+
context.appendScope( QgsExpressionContextUtils::mapSettingsScope( mapCanvas->mapSettings() ) );
419+
420+
// Get the first feature if any, and add it to the expression context
421+
QgsFeature previewFeature;
422+
if ( vlayer->featureCount() > 0 )
423+
{
424+
QgsFeatureIterator it = vlayer->getFeatures( QgsFeatureRequest().setLimit( 1 ) );
425+
it.nextFeature( previewFeature );
426+
}
427+
else
428+
{
429+
previewFeature = QgsFeature( vlayer->fields() );
430+
}
431+
context.setFeature( previewFeature );
432+
433+
// Generate the map tip text from the context and the mapTipTemplate/displayExpression
434+
QString tipText;
435+
if ( mapTemplate.isEmpty() )
436+
{
437+
QgsExpression exp( displayExpression );
438+
exp.prepare( &context );
439+
tipText = exp.evaluate( &context ).toString();
440+
}
441+
else
442+
{
443+
tipText = QgsExpression::replaceExpressionText( mapTemplate, &context );
444+
}
445+
446+
// Insert the map tip text into the html template
447+
return QgsMapTip::htmlText( tipText );
448+
449+
}
450+
451+
QString QgsMapTip::rasterMapTipPreviewText( QgsMapLayer *layer, QgsMapCanvas *mapCanvas, const QString &mapTemplate )
452+
{
453+
QgsRasterLayer *rlayer = qobject_cast<QgsRasterLayer *>( layer );
454+
if ( !mapCanvas || !rlayer || mapTemplate.isEmpty() )
455+
{
456+
return QString();
457+
}
458+
459+
// Create an expression context
460+
QgsExpressionContext context( QgsExpressionContextUtils::globalProjectLayerScopes( layer ) );
461+
context.appendScope( QgsExpressionContextUtils::mapSettingsScope( mapCanvas->mapSettings() ) );
462+
463+
// Get the position of the center of the layer, and add it to the expression context
464+
const QgsPointXY mappedPosition { layer->extent().center() };
465+
context.appendScope( QgsExpressionContextUtils::mapLayerPositionScope( mappedPosition ) );
466+
467+
// Generate the map tip text from the context and the mapTipTemplate
468+
const QString tipText = QgsExpression::replaceExpressionText( mapTemplate, &context );
469+
470+
// Insert the map tip text into the html template
471+
return QgsMapTip::htmlText( tipText );
472+
}

‎src/gui/qgsmaptip.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,20 @@ class GUI_EXPORT QgsMapTip : public QWidget
8686
*/
8787
void applyFontSettings();
8888

89+
/**
90+
* Returns the html that would be displayed in a maptip for a given layer. If the layer has features, the first feature is used
91+
* to evaluate the expressions.
92+
* \since QGIS 3.32
93+
*/
94+
static QString vectorMapTipPreviewText( QgsMapLayer *layer, QgsMapCanvas *mapCanvas, const QString &mapTemplate, const QString &displayExpression );
95+
96+
/**
97+
* Returns the html that would be displayed in a maptip for a given layer. The center pixel of the raster is used to
98+
* evaluate the expressions.
99+
* \since QGIS 3.32
100+
*/
101+
static QString rasterMapTipPreviewText( QgsMapLayer *layer, QgsMapCanvas *mapCanvas, const QString &mapTemplate );
102+
89103
private slots:
90104
void onLinkClicked( const QUrl &url );
91105
void resizeContent();
@@ -102,8 +116,8 @@ class GUI_EXPORT QgsMapTip : public QWidget
102116
QgsPointXY &mapPosition,
103117
QgsMapCanvas *mapCanvas );
104118

105-
QString replaceText(
106-
QString displayText, QgsVectorLayer *layer, QgsFeature &feat );
119+
// Insert the raw map tip text into an HTML template and return the result
120+
static QString htmlText( const QString &text );
107121

108122
// Flag to indicate if a maptip is currently being displayed
109123
bool mMapTipVisible;

‎src/gui/raster/qgsrasterlayerproperties.cpp

Lines changed: 92 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@
6363
#include "qgsrasterattributetablewidget.h"
6464
#include "qgsrasterlayertemporalpropertieswidget.h"
6565
#include "qgsexpressioncontextutils.h"
66+
#include "qgsmaptip.h"
67+
#include "qgswebframe.h"
68+
#if WITH_QTWEBKIT
69+
#include <QWebElement>
70+
#endif
6671

6772
#include <QDesktopServices>
6873
#include <QTableWidgetItem>
@@ -222,7 +227,7 @@ QgsRasterLayerProperties::QgsRasterLayerProperties( QgsMapLayer *lyr, QgsMapCanv
222227
mRasterTransparencyWidget->pbnDefaultValues->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionOpenTable.svg" ) ) );
223228
mRasterTransparencyWidget->pbnImportTransparentPixelValues->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFileOpen.svg" ) ) );
224229
mRasterTransparencyWidget->pbnExportTransparentPixelValues->setIcon( QgsApplication::getThemeIcon( QStringLiteral( "/mActionFileSave.svg" ) ) );
225-
230+
initMapTipPreview();
226231

227232
if ( !mRasterLayer )
228233
{
@@ -1921,3 +1926,89 @@ void QgsRasterLayerProperties::updateGammaSlider( double value )
19211926
{
19221927
whileBlocking( mSliderGamma )->setValue( value * 100 );
19231928
}
1929+
1930+
1931+
bool QgsRasterLayerProperties::eventFilter( QObject *obj, QEvent *ev )
1932+
{
1933+
// If the map tip preview container is resized, resize the map tip
1934+
if ( obj == mMapTipPreviewContainer && ev->type() == QEvent::Resize )
1935+
{
1936+
resizeMapTip();
1937+
}
1938+
return QgsOptionsDialogBase::eventFilter( obj, ev );
1939+
}
1940+
1941+
void QgsRasterLayerProperties::initMapTipPreview()
1942+
{
1943+
// HTML editor and preview are in a splitter. By default, the editor takes 2/3 of the space
1944+
mMapTipSplitter->setSizes( { 400, 200 } );
1945+
// Event filter is used to resize the map tip when the container is resized
1946+
mMapTipPreviewContainer->installEventFilter( this );
1947+
1948+
// Note: there's quite a bit of overlap between this and the code in QgsMapTip::showMapTip
1949+
// Create and style the map tip frame
1950+
mMapTipPreviewWidget = new QWidget( mMapTipPreviewContainer );
1951+
mMapTipPreviewWidget->setContentsMargins( MARGIN_VALUE, MARGIN_VALUE, MARGIN_VALUE, MARGIN_VALUE );
1952+
const QString backgroundColor = mMapTipPreviewWidget->palette().base().color().name();
1953+
const QString strokeColor = mMapTipPreviewWidget->palette().shadow().color().name();
1954+
mMapTipPreviewWidget->setStyleSheet( QString(
1955+
".QWidget{"
1956+
"border: 1px solid %1;"
1957+
"background-color: %2;}" ).arg(
1958+
strokeColor, backgroundColor ) );
1959+
1960+
// Create the WebView
1961+
mMapTipPreview = new QgsWebView( mMapTipPreviewWidget );
1962+
1963+
#if WITH_QTWEBKIT
1964+
mMapTipPreview->page()->setLinkDelegationPolicy( QWebPage::DelegateAllLinks );//Handle link clicks by yourself
1965+
mMapTipPreview->setContextMenuPolicy( Qt::NoContextMenu ); //No context menu is allowed if you don't need it
1966+
connect( mMapTipPreview, &QWebView::loadFinished, this, &QgsRasterLayerProperties::resizeMapTip );
1967+
#endif
1968+
1969+
mMapTipPreview->page()->settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true );
1970+
mMapTipPreview->page()->settings()->setAttribute( QWebSettings::JavascriptEnabled, true );
1971+
mMapTipPreview->page()->settings()->setAttribute( QWebSettings::LocalStorageEnabled, true );
1972+
1973+
// Disable scrollbars, avoid random resizing issues
1974+
mMapTipPreview->page()->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
1975+
mMapTipPreview->page()->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
1976+
1977+
QHBoxLayout *hlayout = new QHBoxLayout;
1978+
hlayout->setContentsMargins( 0, 0, 0, 0 );
1979+
hlayout->addWidget( mMapTipPreview );
1980+
1981+
mMapTipPreviewWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
1982+
mMapTipPreviewWidget->setLayout( hlayout );
1983+
1984+
// Update the map tip preview when the expression or the map tip template changes
1985+
connect( mMapTipWidget, &QgsCodeEditorHTML::textChanged, this, &QgsRasterLayerProperties::updateMapTipPreview );
1986+
}
1987+
1988+
void QgsRasterLayerProperties::updateMapTipPreview()
1989+
{
1990+
mMapTipPreviewWidget->setMaximumSize( mMapTipPreviewContainer->width(), mMapTipPreviewContainer->height() );
1991+
const QString htmlContent = QgsMapTip::rasterMapTipPreviewText( mRasterLayer, mMapCanvas, mMapTipWidget->text() );
1992+
mMapTipPreview->setHtml( htmlContent );
1993+
}
1994+
1995+
void QgsRasterLayerProperties::resizeMapTip()
1996+
{
1997+
// Ensure the map tip is not bigger than the container
1998+
mMapTipPreviewWidget->setMaximumSize( mMapTipPreviewContainer->width(), mMapTipPreviewContainer->height() );
1999+
#if WITH_QTWEBKIT
2000+
// Get the content size
2001+
const QWebElement container = mMapTipPreview->page()->mainFrame()->findFirstElement(
2002+
QStringLiteral( "#QgsWebViewContainer" ) );
2003+
const int width = container.geometry().width() + MARGIN_VALUE * 2;
2004+
const int height = container.geometry().height() + MARGIN_VALUE * 2;
2005+
mMapTipPreviewWidget->resize( width, height );
2006+
2007+
// Move the map tip to the center of the container
2008+
mMapTipPreviewWidget->move( ( mMapTipPreviewContainer->width() - mMapTipPreviewWidget->width() ) / 2,
2009+
( mMapTipPreviewContainer->height() - mMapTipPreviewWidget->height() ) / 2 );
2010+
2011+
#else
2012+
mMapTipPreview->adjustSize();
2013+
#endif
2014+
}

‎src/gui/raster/qgsrasterlayerproperties.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ class QgsMapLayerConfigWidget;
4848
class QgsPropertyOverrideButton;
4949
class QgsRasterTransparencyWidget;
5050
class QgsRasterAttributeTableWidget;
51+
class QgsWebView;
5152

5253

5354
/**
@@ -92,6 +93,8 @@ class GUI_EXPORT QgsRasterLayerProperties : public QgsOptionsDialogBase, private
9293

9394
QgsExpressionContext createExpressionContext() const override;
9495

96+
bool eventFilter( QObject *obj, QEvent *ev ) override;
97+
9598
protected slots:
9699
//! \brief auto slot executed when the active page in the main widget stack is changed
97100
void optionsStackedWidget_CurrentChanged( int index ) override SIP_SKIP ;
@@ -199,6 +202,11 @@ class GUI_EXPORT QgsRasterLayerProperties : public QgsOptionsDialogBase, private
199202

200203
void urlClicked( const QUrl &url );
201204

205+
// Update the preview of the map tip
206+
void updateMapTipPreview();
207+
// Resize the map tip preview
208+
void resizeMapTip();
209+
202210
private:
203211
QPushButton *mBtnStyle = nullptr;
204212
QPushButton *mBtnMetadata = nullptr;
@@ -305,5 +313,11 @@ class GUI_EXPORT QgsRasterLayerProperties : public QgsOptionsDialogBase, private
305313
QgsCoordinateReferenceSystem mBackupCrs;
306314

307315
QgsRasterAttributeTableWidget *mRasterAttributeTableWidget = nullptr;
316+
317+
void initMapTipPreview();
318+
319+
QWidget *mMapTipPreviewWidget = nullptr;
320+
QgsWebView *mMapTipPreview = nullptr;
321+
static const int MARGIN_VALUE = 5;
308322
};
309323
#endif

‎src/gui/vector/qgsvectorlayerproperties.cpp

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@
6666
#include "qgsproviderregistry.h"
6767
#include "qgsmaplayerstylemanager.h"
6868
#include "qgslayertreemodel.h"
69+
#include "qgsmaptip.h"
70+
#include "qgswebview.h"
71+
#include "qgswebframe.h"
72+
#if WITH_QTWEBKIT
73+
#include <QWebElement>
74+
#endif
6975

7076
#include <QDesktopServices>
7177
#include <QMessageBox>
@@ -162,6 +168,7 @@ QgsVectorLayerProperties::QgsVectorLayerProperties(
162168
mMapTipExpressionFieldWidget->registerExpressionContextGenerator( this );
163169
mDisplayExpressionWidget->setLayer( lyr );
164170
mDisplayExpressionWidget->registerExpressionContextGenerator( this );
171+
initMapTipPreview();
165172

166173
connect( mInsertExpressionButton, &QAbstractButton::clicked, this, &QgsVectorLayerProperties::insertFieldOrExpression );
167174

@@ -2248,3 +2255,89 @@ void QgsVectorLayerProperties::deleteAuxiliaryField( int index )
22482255
mMessageBar->pushMessage( title, msg, Qgis::MessageLevel::Warning );
22492256
}
22502257
}
2258+
2259+
bool QgsVectorLayerProperties::eventFilter( QObject *obj, QEvent *ev )
2260+
{
2261+
// If the map tip preview container is resized, resize the map tip
2262+
if ( obj == mMapTipPreviewContainer && ev->type() == QEvent::Resize )
2263+
{
2264+
resizeMapTip();
2265+
}
2266+
return QgsOptionsDialogBase::eventFilter( obj, ev );
2267+
}
2268+
2269+
void QgsVectorLayerProperties::initMapTipPreview()
2270+
{
2271+
// HTML editor and preview are in a splitter. By default, the editor takes 2/3 of the space
2272+
mMapTipSplitter->setSizes( { 400, 200 } );
2273+
// Event filter is used to resize the map tip when the container is resized
2274+
mMapTipPreviewContainer->installEventFilter( this );
2275+
2276+
// Note: there's quite a bit of overlap between this and the code in QgsMapTip::showMapTip
2277+
// Create and style the map tip frame
2278+
mMapTipPreviewWidget = new QWidget( mMapTipPreviewContainer );
2279+
mMapTipPreviewWidget->setContentsMargins( MARGIN_VALUE, MARGIN_VALUE, MARGIN_VALUE, MARGIN_VALUE );
2280+
const QString backgroundColor = mMapTipPreviewWidget->palette().base().color().name();
2281+
const QString strokeColor = mMapTipPreviewWidget->palette().shadow().color().name();
2282+
mMapTipPreviewWidget->setStyleSheet( QString(
2283+
".QWidget{"
2284+
"border: 1px solid %1;"
2285+
"background-color: %2;}" ).arg(
2286+
strokeColor, backgroundColor ) );
2287+
2288+
// Create the WebView
2289+
mMapTipPreview = new QgsWebView( mMapTipPreviewWidget );
2290+
2291+
#if WITH_QTWEBKIT
2292+
mMapTipPreview->page()->setLinkDelegationPolicy( QWebPage::DelegateAllLinks );//Handle link clicks by yourself
2293+
mMapTipPreview->setContextMenuPolicy( Qt::NoContextMenu ); //No context menu is allowed if you don't need it
2294+
connect( mMapTipPreview, &QWebView::loadFinished, this, &QgsVectorLayerProperties::resizeMapTip );
2295+
#endif
2296+
2297+
mMapTipPreview->page()->settings()->setAttribute( QWebSettings::DeveloperExtrasEnabled, true );
2298+
mMapTipPreview->page()->settings()->setAttribute( QWebSettings::JavascriptEnabled, true );
2299+
mMapTipPreview->page()->settings()->setAttribute( QWebSettings::LocalStorageEnabled, true );
2300+
2301+
// Disable scrollbars, avoid random resizing issues
2302+
mMapTipPreview->page()->mainFrame()->setScrollBarPolicy( Qt::Horizontal, Qt::ScrollBarAlwaysOff );
2303+
mMapTipPreview->page()->mainFrame()->setScrollBarPolicy( Qt::Vertical, Qt::ScrollBarAlwaysOff );
2304+
2305+
QHBoxLayout *hlayout = new QHBoxLayout;
2306+
hlayout->setContentsMargins( 0, 0, 0, 0 );
2307+
hlayout->addWidget( mMapTipPreview );
2308+
2309+
mMapTipPreviewWidget->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding );
2310+
mMapTipPreviewWidget->setLayout( hlayout );
2311+
2312+
// Update the map tip preview when the expression or the map tip template changes
2313+
connect( mMapTipWidget, &QgsCodeEditorHTML::textChanged, this, &QgsVectorLayerProperties::updateMapTipPreview );
2314+
connect( mDisplayExpressionWidget, qOverload< const QString & >( &QgsFieldExpressionWidget::fieldChanged ), this, &QgsVectorLayerProperties::updateMapTipPreview );
2315+
}
2316+
2317+
void QgsVectorLayerProperties::updateMapTipPreview()
2318+
{
2319+
mMapTipPreviewWidget->setMaximumSize( mMapTipPreviewContainer->width(), mMapTipPreviewContainer->height() );
2320+
const QString htmlContent = QgsMapTip::vectorMapTipPreviewText( mLayer, mCanvas, mMapTipWidget->text(), mDisplayExpressionWidget->asExpression() );
2321+
mMapTipPreview->setHtml( htmlContent );
2322+
}
2323+
2324+
void QgsVectorLayerProperties::resizeMapTip()
2325+
{
2326+
// Ensure the map tip is not bigger than the container
2327+
mMapTipPreviewWidget->setMaximumSize( mMapTipPreviewContainer->width(), mMapTipPreviewContainer->height() );
2328+
#if WITH_QTWEBKIT
2329+
// Get the content size
2330+
const QWebElement container = mMapTipPreview->page()->mainFrame()->findFirstElement(
2331+
QStringLiteral( "#QgsWebViewContainer" ) );
2332+
const int width = container.geometry().width() + MARGIN_VALUE * 2;
2333+
const int height = container.geometry().height() + MARGIN_VALUE * 2;
2334+
mMapTipPreviewWidget->resize( width, height );
2335+
2336+
// Move the map tip to the center of the container
2337+
mMapTipPreviewWidget->move( ( mMapTipPreviewContainer->width() - mMapTipPreviewWidget->width() ) / 2,
2338+
( mMapTipPreviewContainer->height() - mMapTipPreviewWidget->height() ) / 2 );
2339+
2340+
#else
2341+
mMapTipPreview->adjustSize();
2342+
#endif
2343+
}

‎src/gui/vector/qgsvectorlayerproperties.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ class QgsDoubleSpinBox;
4949
class QgsMaskingWidget;
5050
class QgsVectorLayerTemporalPropertiesWidget;
5151
class QgsProviderSourceWidget;
52+
class QgsWebView;
5253

5354
/**
5455
* \ingroup gui
@@ -75,6 +76,8 @@ class GUI_EXPORT QgsVectorLayerProperties : public QgsOptionsDialogBase, private
7576
//! Adds a properties page factory to the vector layer properties dialog.
7677
void addPropertiesPageFactory( const QgsMapLayerConfigWidgetFactory *factory );
7778

79+
bool eventFilter( QObject *obj, QEvent *ev ) override;
80+
7881
protected slots:
7982
void optionsStackedWidget_CurrentChanged( int index ) final;
8083

@@ -162,6 +165,11 @@ class GUI_EXPORT QgsVectorLayerProperties : public QgsOptionsDialogBase, private
162165

163166
void urlClicked( const QUrl &url );
164167

168+
// Update the preview of the map tip
169+
void updateMapTipPreview();
170+
// Resize the map tip preview
171+
void resizeMapTip();
172+
165173
private:
166174

167175
enum PropertyType
@@ -255,6 +263,12 @@ class GUI_EXPORT QgsVectorLayerProperties : public QgsOptionsDialogBase, private
255263

256264
QgsCoordinateReferenceSystem mBackupCrs;
257265

266+
void initMapTipPreview();
267+
268+
QWidget *mMapTipPreviewWidget = nullptr;
269+
QgsWebView *mMapTipPreview = nullptr;
270+
static const int MARGIN_VALUE = 5;
271+
258272
private slots:
259273
void openPanel( QgsPanelWidget *panel );
260274

‎src/ui/qgsrasterlayerpropertiesbase.ui

Lines changed: 47 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,8 +1487,46 @@ p, li { white-space: pre-wrap; }
14871487
<property name="title">
14881488
<string>HTML Map Tip</string>
14891489
</property>
1490-
<layout class="QGridLayout" name="gridLayout_81">
1491-
<item row="1" column="2">
1490+
<layout class="QGridLayout" name="gridLayout_8">
1491+
<item row="0" column="0" colspan="2">
1492+
<widget class="QSplitter" name="mMapTipSplitter">
1493+
<property name="sizePolicy">
1494+
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1495+
<horstretch>0</horstretch>
1496+
<verstretch>0</verstretch>
1497+
</sizepolicy>
1498+
</property>
1499+
<property name="orientation">
1500+
<enum>Qt::Horizontal</enum>
1501+
</property>
1502+
<widget class="QgsCodeEditorHTML" name="mMapTipWidget" native="true">
1503+
<property name="sizePolicy">
1504+
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
1505+
<horstretch>0</horstretch>
1506+
<verstretch>0</verstretch>
1507+
</sizepolicy>
1508+
</property>
1509+
<property name="minimumSize">
1510+
<size>
1511+
<width>100</width>
1512+
<height>100</height>
1513+
</size>
1514+
</property>
1515+
</widget>
1516+
<widget class="QGroupBox" name="mMapTipPreviewContainer">
1517+
<property name="minimumSize">
1518+
<size>
1519+
<width>100</width>
1520+
<height>100</height>
1521+
</size>
1522+
</property>
1523+
<property name="title">
1524+
<string>Preview</string>
1525+
</property>
1526+
</widget>
1527+
</widget>
1528+
</item>
1529+
<item row="1" column="1">
14921530
<widget class="QPushButton" name="mInsertExpressionButton">
14931531
<property name="sizePolicy">
14941532
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@@ -1504,23 +1542,17 @@ p, li { white-space: pre-wrap; }
15041542
</property>
15051543
</widget>
15061544
</item>
1507-
<item row="0" column="0" colspan="3">
1508-
<widget class="QgsCodeEditorHTML" name="mMapTipWidget" native="true">
1509-
<property name="sizePolicy">
1510-
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
1511-
<horstretch>0</horstretch>
1512-
<verstretch>0</verstretch>
1513-
</sizepolicy>
1545+
<item row="2" column="0" colspan="2">
1546+
<widget class="QLabel" name="labelMapTipInfo">
1547+
<property name="text">
1548+
<string>The HTML map tips are shown when moving mouse over features of the currently selected layer when the 'Show Map Tips' action is toggled on. If no HTML code is set, the feature display name is used.</string>
15141549
</property>
1515-
<property name="minimumSize">
1516-
<size>
1517-
<width>0</width>
1518-
<height>100</height>
1519-
</size>
1550+
<property name="wordWrap">
1551+
<bool>true</bool>
15201552
</property>
15211553
</widget>
15221554
</item>
1523-
<item row="1" column="0" colspan="2">
1555+
<item row="1" column="0">
15241556
<widget class="QgsExpressionLineEdit" name="mMapTipExpressionWidget" native="true">
15251557
<property name="focusPolicy">
15261558
<enum>Qt::StrongFocus</enum>
@@ -1530,16 +1562,6 @@ p, li { white-space: pre-wrap; }
15301562
</property>
15311563
</widget>
15321564
</item>
1533-
<item row="3" column="0" colspan="3">
1534-
<widget class="QLabel" name="labelMapTipInfo">
1535-
<property name="text">
1536-
<string>The HTML map tips are shown when moving mouse over features of the currently selected layer when the 'Show Map Tips' action is toggled on.</string>
1537-
</property>
1538-
<property name="wordWrap">
1539-
<bool>true</bool>
1540-
</property>
1541-
</widget>
1542-
</item>
15431565
</layout>
15441566
</widget>
15451567
</item>

‎src/ui/qgsvectorlayerpropertiesbase.ui

Lines changed: 55 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@
363363
</sizepolicy>
364364
</property>
365365
<property name="currentIndex">
366-
<number>11</number>
366+
<number>0</number>
367367
</property>
368368
<widget class="QWidget" name="mOptsPage_Information">
369369
<layout class="QVBoxLayout" name="verticalLayout_5">
@@ -439,8 +439,8 @@
439439
<rect>
440440
<x>0</x>
441441
<y>0</y>
442-
<width>239</width>
443-
<height>480</height>
442+
<width>336</width>
443+
<height>562</height>
444444
</rect>
445445
</property>
446446
<layout class="QVBoxLayout" name="verticalLayout_9">
@@ -950,8 +950,8 @@
950950
<rect>
951951
<x>0</x>
952952
<y>0</y>
953-
<width>110</width>
954-
<height>87</height>
953+
<width>164</width>
954+
<height>103</height>
955955
</rect>
956956
</property>
957957
<layout class="QVBoxLayout" name="verticalLayout_23">
@@ -1460,7 +1460,45 @@
14601460
<string>HTML Map Tip</string>
14611461
</property>
14621462
<layout class="QGridLayout" name="gridLayout_8">
1463-
<item row="1" column="2">
1463+
<item row="0" column="0" colspan="2">
1464+
<widget class="QSplitter" name="mMapTipSplitter">
1465+
<property name="sizePolicy">
1466+
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
1467+
<horstretch>0</horstretch>
1468+
<verstretch>0</verstretch>
1469+
</sizepolicy>
1470+
</property>
1471+
<property name="orientation">
1472+
<enum>Qt::Horizontal</enum>
1473+
</property>
1474+
<widget class="QgsCodeEditorHTML" name="mMapTipWidget" native="true">
1475+
<property name="sizePolicy">
1476+
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
1477+
<horstretch>0</horstretch>
1478+
<verstretch>0</verstretch>
1479+
</sizepolicy>
1480+
</property>
1481+
<property name="minimumSize">
1482+
<size>
1483+
<width>100</width>
1484+
<height>100</height>
1485+
</size>
1486+
</property>
1487+
</widget>
1488+
<widget class="QGroupBox" name="mMapTipPreviewContainer">
1489+
<property name="minimumSize">
1490+
<size>
1491+
<width>100</width>
1492+
<height>100</height>
1493+
</size>
1494+
</property>
1495+
<property name="title">
1496+
<string>Preview</string>
1497+
</property>
1498+
</widget>
1499+
</widget>
1500+
</item>
1501+
<item row="1" column="1">
14641502
<widget class="QPushButton" name="mInsertExpressionButton">
14651503
<property name="sizePolicy">
14661504
<sizepolicy hsizetype="Fixed" vsizetype="Fixed">
@@ -1476,23 +1514,17 @@
14761514
</property>
14771515
</widget>
14781516
</item>
1479-
<item row="0" column="0" colspan="3">
1480-
<widget class="QgsCodeEditorHTML" name="mMapTipWidget" native="true">
1481-
<property name="sizePolicy">
1482-
<sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
1483-
<horstretch>0</horstretch>
1484-
<verstretch>0</verstretch>
1485-
</sizepolicy>
1517+
<item row="2" column="0" colspan="2">
1518+
<widget class="QLabel" name="labelMapTipInfo">
1519+
<property name="text">
1520+
<string>The HTML map tips are shown when moving mouse over features of the currently selected layer when the 'Show Map Tips' action is toggled on. If no HTML code is set, the feature display name is used.</string>
14861521
</property>
1487-
<property name="minimumSize">
1488-
<size>
1489-
<width>0</width>
1490-
<height>100</height>
1491-
</size>
1522+
<property name="wordWrap">
1523+
<bool>true</bool>
14921524
</property>
14931525
</widget>
14941526
</item>
1495-
<item row="1" column="0" colspan="2">
1527+
<item row="1" column="0">
14961528
<widget class="QgsFieldExpressionWidget" name="mMapTipExpressionFieldWidget" native="true">
14971529
<property name="focusPolicy">
14981530
<enum>Qt::StrongFocus</enum>
@@ -1502,16 +1534,6 @@
15021534
</property>
15031535
</widget>
15041536
</item>
1505-
<item row="3" column="0" colspan="3">
1506-
<widget class="QLabel" name="labelMapTipInfo">
1507-
<property name="text">
1508-
<string>The HTML map tips are shown when moving mouse over features of the currently selected layer when the 'Show Map Tips' action is toggled on. If no HTML code is set, the feature display name is used.</string>
1509-
</property>
1510-
<property name="wordWrap">
1511-
<bool>true</bool>
1512-
</property>
1513-
</widget>
1514-
</item>
15151537
</layout>
15161538
</widget>
15171539
</item>
@@ -1547,8 +1569,8 @@
15471569
<rect>
15481570
<x>0</x>
15491571
<y>0</y>
1550-
<width>493</width>
1551-
<height>473</height>
1572+
<width>695</width>
1573+
<height>552</height>
15521574
</rect>
15531575
</property>
15541576
<layout class="QVBoxLayout" name="verticalLayout_32">
@@ -2106,8 +2128,8 @@
21062128
<rect>
21072129
<x>0</x>
21082130
<y>0</y>
2109-
<width>263</width>
2110-
<height>713</height>
2131+
<width>374</width>
2132+
<height>815</height>
21112133
</rect>
21122134
</property>
21132135
<layout class="QVBoxLayout" name="verticalLayout_13">

0 commit comments

Comments
 (0)
Please sign in to comment.