diff -r 6e8b807bda4b -r ba39a1d396c0 QTfrontend/ui/widget/mapContainer.cpp --- a/QTfrontend/ui/widget/mapContainer.cpp Sun Jun 10 18:56:51 2018 +0200 +++ b/QTfrontend/ui/widget/mapContainer.cpp Sun Jun 10 19:12:26 2018 +0200 @@ -64,15 +64,28 @@ m_prevMapFeatureSize = 12; m_mapFeatureSize = 12; m_withoutDLC = false; + m_missingMap = false; hhSmall.load(":/res/hh_small.png"); hhLimit = 18; templateFilter = 0; m_master = true; - linearGrad = QLinearGradient(QPoint(128, 0), QPoint(128, 128)); - linearGrad.setColorAt(1, QColor(0, 0, 192)); - linearGrad.setColorAt(0, QColor(66, 115, 225)); + linearGradNormal = QLinearGradient(QPoint(128, 0), QPoint(128, 128)); + linearGradNormal.setColorAt(1, QColor(0, 0, 192)); + linearGradNormal.setColorAt(0, QColor(66, 115, 225)); + + linearGradLoading = QLinearGradient(QPoint(128, 0), QPoint(128, 128)); + linearGradLoading.setColorAt(1, QColor(58, 58, 137)); + linearGradLoading.setColorAt(0, QColor(90, 109, 153)); + + linearGradMapError = QLinearGradient(QPoint(128, 0), QPoint(128, 128)); + linearGradMapError.setColorAt(1, QColor(255, 1, 0)); + linearGradMapError.setColorAt(0, QColor(255, 119, 0)); + + linearGradNoPreview = QLinearGradient(QPoint(128, 0), QPoint(128, 128)); + linearGradNoPreview.setColorAt(1, QColor(15, 9, 72)); + linearGradNoPreview.setColorAt(0, QColor(15, 9, 72)); mainLayout.setContentsMargins(HWApplication::style()->pixelMetric(QStyle::PM_LayoutLeftMargin), 10, @@ -90,18 +103,23 @@ topWidget->setContentsMargins(0, 0, 0, 0); topLayout->setContentsMargins(0, 0, 0, 0); - QHBoxLayout * twoColumnLayout = new QHBoxLayout(); + twoColumnLayout = new QHBoxLayout(); QVBoxLayout * leftLayout = new QVBoxLayout(); + leftLayout->setAlignment(Qt::AlignLeft); QVBoxLayout * rightLayout = new QVBoxLayout(); twoColumnLayout->addLayout(leftLayout, 0); - twoColumnLayout->addStretch(1); twoColumnLayout->addLayout(rightLayout, 0); QVBoxLayout * drawnControls = new QVBoxLayout(); + /* Map type label */ + + QLabel* lblMapType = new QLabel(tr("Map type:")); + topLayout->setSpacing(10); + topLayout->addWidget(lblMapType, 0); + m_childWidgets << lblMapType; + /* Map type combobox */ - topLayout->setSpacing(10); - topLayout->addWidget(new QLabel(tr("Map type:")), 0); cType = new QComboBox(this); topLayout->addWidget(cType, 1); cType->insertItem(0, tr("Image map"), MapModel::StaticMap); @@ -148,6 +166,7 @@ QLabel * lblMapPreviewText = new QLabel(this); lblMapPreviewText->setText(tr("Map preview:")); leftLayout->addWidget(lblMapPreviewText, 0); + m_childWidgets << lblMapPreviewText; /* Map Preview */ @@ -158,6 +177,7 @@ mapPreview->setContentsMargins(0, 0, 0, 0); leftLayout->addWidget(mapPreview, 0); connect(mapPreview, SIGNAL(clicked()), this, SLOT(previewClicked())); + m_childWidgets << mapPreview; /* Bottom-Left layout */ @@ -167,7 +187,10 @@ /* Map list label */ lblMapList = new QLabel(this); + lblMapList->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum); + lblMapList->setAlignment(Qt::AlignTop | Qt::AlignLeft); rightLayout->addWidget(lblMapList, 0); + m_childWidgets << lblMapList; /* Static maps list */ @@ -181,6 +204,26 @@ rightLayout->addWidget(missionMapList, 1); m_childWidgets << missionMapList; + /* Map name (when not room master) */ + /* We use a QTextEdit instead of QLabel because it is able + to wrap at any character. */ + teMapName = new QTextEdit(this); + teMapName->setObjectName("mapName"); + teMapName->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Minimum); + teMapName->setAlignment(Qt::AlignTop | Qt::AlignLeft); + + /* Boilerplate to emulate a QLabel */ + teMapName->setReadOnly(true); + teMapName->setAcceptRichText(false); + teMapName->setFrameStyle(QFrame::NoFrame); + teMapName->setStyleSheet("background-color: transparent"); + + teMapName->setLineWrapMode(QTextEdit::WidgetWidth); + teMapName->setWordWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere); + + rightLayout->addWidget(teMapName, 1); + m_childWidgets << teMapName; + /* Map load and edit buttons */ drawnControls->addStretch(1); @@ -234,7 +277,7 @@ mapFeatureSize->setMinimum(1); //mapFeatureSize->setFixedWidth(259); mapFeatureSize->setValue(m_mapFeatureSize); - mapFeatureSize->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + mapFeatureSize->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Expanding); bottomLeftLayout->addWidget(mapFeatureSize, 0); connect(mapFeatureSize, SIGNAL(valueChanged(int)), this, SLOT(setFeatureSize(int))); m_childWidgets << mapFeatureSize; @@ -244,7 +287,7 @@ lblDesc = new QLabel(); lblDesc->setWordWrap(true); lblDesc->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - lblDesc->setAlignment(Qt::AlignTop | Qt::AlignLeft); + lblDesc->setAlignment(Qt::AlignBottom | Qt::AlignLeft); lblDesc->setStyleSheet("font: 10px;"); bottomLeftLayout->addWidget(lblDesc, 100); @@ -292,6 +335,24 @@ changeMapType(MapModel::GeneratedMap); } +void HWMapContainer::onImageReceived(const QPixmap &newImage) +{ + // When image received from the engine. + switch (m_mapInfo.type) + { + case MapModel::GeneratedMap: + case MapModel::GeneratedMaze: + case MapModel::GeneratedPerlin: + case MapModel::HandDrawnMap: + case MapModel::FortsMap: + setImage(newImage); + break; + // Throw away image if we have switched the map mode in the meantime + default: + return; + } +} + void HWMapContainer::setImage(const QPixmap &newImage) { addInfoToPreview(newImage); @@ -300,43 +361,61 @@ cType->setEnabled(isMaster()); } +void HWMapContainer::setImage(const QPixmap &newImage, const QLinearGradient &linearGrad, bool showHHLimit) +{ + addInfoToPreview(newImage, linearGrad, showHHLimit); + + pMap = 0; + + cType->setEnabled(isMaster()); +} + + void HWMapContainer::setHHLimit(int newHHLimit) { hhLimit = newHHLimit; } +void HWMapContainer::addInfoToPreview(const QPixmap &image) +{ + addInfoToPreview(image, linearGradNormal, true); +} + // Should this add text to identify map size? -void HWMapContainer::addInfoToPreview(const QPixmap &image) +void HWMapContainer::addInfoToPreview(const QPixmap &image, const QLinearGradient &linearGrad, bool drawHHLimit) { QPixmap finalImage = QPixmap(image.size()); -//finalImage.fill(QColor(0, 0, 0, 0)); + QPainter p(&finalImage); - QPainter p(&finalImage); p.fillRect(finalImage.rect(), linearGrad); p.drawPixmap(finalImage.rect(), image); - //p.setPen(QColor(0xf4,0x9e,0xe9)); - p.setPen(QColor(0xff,0xcc,0x00)); - p.setBrush(QColor(0, 0, 0)); - p.drawRect(finalImage.rect().width() - hhSmall.rect().width() - 28, 3, 40, 20); - p.setFont(QFont("MS Shell Dlg", 10)); - QString text = (hhLimit > 0) ? QString::number(hhLimit) : "?"; - p.drawText(finalImage.rect().width() - hhSmall.rect().width() - 14 - (hhLimit > 9 ? 10 : 0), 18, text); - p.drawPixmap(finalImage.rect().width() - hhSmall.rect().width() - 5, 5, hhSmall.rect().width(), hhSmall.rect().height(), hhSmall); + + if (drawHHLimit) + { + p.setPen(QColor(0xff,0xcc,0x00)); + p.setBrush(QColor(0, 0, 0)); + p.setFont(QFont("MS Shell Dlg", 10)); + + p.drawRect(finalImage.rect().width() - hhSmall.rect().width() - 28, 3, 40, 20); - // Shrink, crop, and center preview image - /*QPixmap centered(QSize(m_previewSize.width() - 6, m_previewSize.height() - 6)); - QPainter pc(¢ered); - pc.fillRect(centered.rect(), linearGrad); - pc.drawPixmap(-3, -3, finalImage);*/ + QString text = (hhLimit > 0) ? QString::number(hhLimit) : "?"; + p.drawText(finalImage.rect().width() - hhSmall.rect().width() - 14 - (hhLimit > 9 ? 10 : 0), 18, text); + p.drawPixmap(finalImage.rect().width() - hhSmall.rect().width() - 5, 5, hhSmall.rect().width(), hhSmall.rect().height(), hhSmall); + } - mapPreview->setIcon(QIcon(finalImage)); + // Set the map preview image. Make sure it is always colored the same, + // no matter if disabled or not. + QIcon mapPreviewIcon = QIcon(); + mapPreviewIcon.addPixmap(finalImage, QIcon::Normal); + mapPreviewIcon.addPixmap(finalImage, QIcon::Disabled); + mapPreview->setIcon(mapPreviewIcon); mapPreview->setIconSize(finalImage.size()); } void HWMapContainer::askForGeneratedPreview() { pMap = new HWMap(this); - connect(pMap, SIGNAL(ImageReceived(QPixmap)), this, SLOT(setImage(QPixmap))); + connect(pMap, SIGNAL(ImageReceived(QPixmap)), this, SLOT(onImageReceived(const QPixmap))); connect(pMap, SIGNAL(HHLimitReceived(int)), this, SLOT(setHHLimit(int))); connect(pMap, SIGNAL(destroyed(QObject *)), this, SLOT(onPreviewMapDestroyed(QObject *))); pMap->getImage(m_seed, @@ -351,17 +430,16 @@ setHHLimit(0); - const QPixmap waitIcon(":/res/iconTime.png"); + QPixmap waitImage(m_previewSize); + waitImage.fill(Qt::transparent); - QPixmap waitImage(m_previewSize); QPainter p(&waitImage); - - p.fillRect(waitImage.rect(), linearGrad); + const QPixmap waitIcon(":/res/iconTime.png"); int x = (waitImage.width() - waitIcon.width()) / 2; int y = (waitImage.height() - waitIcon.height()) / 2; p.drawPixmap(QPoint(x, y), waitIcon); - addInfoToPreview(waitImage); + setImage(waitImage, linearGradLoading, false); cType->setEnabled(false); } @@ -486,20 +564,37 @@ } else if (m_staticMapModel->mapExists(map)) { + m_missingMap = false; changeMapType(MapModel::StaticMap, m_staticMapModel->index(m_staticMapModel->findMap(map), 0)); } else if (m_missionMapModel->mapExists(map)) { + m_missingMap = false; changeMapType(MapModel::MissionMap, m_missionMapModel->index(m_missionMapModel->findMap(map), 0)); } else { qDebug() << "HWMapContainer::intSetMap: Map doesn't exist: " << map; + m_missingMap = true; + m_curMap = map; + m_mapInfo.name = map; + setMapNameLabel(map, false); + if (m_mapInfo.type == MapModel::StaticMap) + setupStaticMapsView(m_curMap); + else if (m_mapInfo.type == MapModel::MissionMap) + setupMissionMapsView(m_curMap); + else + { + m_mapInfo.type = MapModel::StaticMap; + setupStaticMapsView(m_curMap); + changeMapType(m_mapInfo.type, QModelIndex()); + } + updatePreview(); } } void HWMapContainer::setMap(const QString & map) { - if ((m_mapInfo.type == MapModel::Invalid) || (map != m_mapInfo.name)) + if ((m_mapInfo.type == MapModel::Invalid) || (map != m_mapInfo.name) || m_missingMap) intSetMap(map); } @@ -510,7 +605,7 @@ if(mdl.size()) updateTheme(mdl.at(0)); else - intSetIconlessTheme(theme); + setMissingTheme(theme); } void HWMapContainer::setRandomMap() @@ -568,9 +663,9 @@ QAbstractItemModel * tmodel; if (m_withoutDLC) - tmodel = m_themeModel->withoutDLC(); + tmodel = m_themeModel->withoutDLCOrHidden(); else - tmodel = m_themeModel; + tmodel = m_themeModel->withoutHidden(); if(!tmodel->rowCount()) return; quint32 themeNum = rand() % tmodel->rowCount(); @@ -701,7 +796,8 @@ void HWMapContainer::showEvent(QShowEvent * event) { - if (!m_previewEnabled) { + if (!m_previewEnabled) + { m_previewEnabled = true; setRandomTheme(); updatePreview(); @@ -717,19 +813,22 @@ if (pMap) { - disconnect(pMap, 0, this, SLOT(setImage(const QPixmap))); + disconnect(pMap, 0, this, SLOT(onImageReceived(const QPixmap))); disconnect(pMap, 0, this, SLOT(setHHLimit(int))); + disconnect(pMap, 0, this, SLOT(onPreviewMapDestroyed(QObject *))); pMap = 0; } - QPixmap failIcon; + QPixmap failPixmap; + QIcon failIcon; switch(m_mapInfo.type) { case MapModel::Invalid: - failIcon = QPixmap(":/res/btnDisabled.png"); - mapPreview->setIcon(QIcon(failIcon)); - mapPreview->setIconSize(failIcon.size()); + // Map error image + failPixmap = QPixmap(":/res/missingMap.png"); + setImage(failPixmap, linearGradMapError, false); + lblDesc->clear(); break; case MapModel::GeneratedMap: case MapModel::GeneratedMaze: @@ -739,17 +838,32 @@ askForGeneratedPreview(); break; default: - QPixmap mapImage; - bool success = mapImage.load("physfs://Maps/" + m_mapInfo.name + "/preview.png"); - - if(!success) + // For maps loaded from image + if(m_missingMap) + { + // Map error image due to missing map + failPixmap = QPixmap(":/res/missingMap.png"); + setImage(failPixmap, linearGradMapError, false); + lblDesc->clear(); + break; + } + else { - mapPreview->setIcon(QIcon()); - return; + // Draw map preview + QPixmap mapImage; + bool success = mapImage.load("physfs://Maps/" + m_mapInfo.name + "/preview.png"); + + setHHLimit(m_mapInfo.limit); + if(!success) + { + // Missing preview image + QPixmap empty = QPixmap(m_previewSize); + empty.fill(Qt::transparent); + setImage(empty, linearGradNoPreview, true); + return; + } + setImage(mapImage); } - - hhLimit = m_mapInfo.limit; - addInfoToPreview(mapImage); } } @@ -769,7 +883,7 @@ { // restore theme selection // do this before map selection restore, because map may overwrite theme - if (!m_theme.isEmpty()) + if (!m_theme.isNull() && !m_theme.isEmpty()) { QModelIndexList mdl = m_themeModel->match(m_themeModel->index(0), Qt::DisplayRole, m_theme); if (mdl.size() > 0) @@ -779,7 +893,7 @@ } // restore map selection - if (!m_curMap.isEmpty()) + if (!m_curMap.isNull() && !m_curMap.isEmpty()) intSetMap(m_curMap); else updatePreview(); @@ -839,6 +953,7 @@ { staticMapList->hide(); missionMapList->hide(); + teMapName->hide(); lblMapList->hide(); generationStyles->hide(); mazeStyles->hide(); @@ -883,7 +998,15 @@ missionMapChanged(newMap.isValid() ? newMap : missionMapList->currentIndex()); lblMapList->setText(tr("Mission:")); lblMapList->show(); - missionMapList->show(); + setMapNameLabel(m_curMap, !m_missingMap); + if(m_master) + { + missionMapList->show(); + } + else + { + teMapName->show(); + } mapFeatureSize->hide(); lblDesc->setText(m_mapInfo.desc); lblDesc->show(); @@ -895,8 +1018,16 @@ staticMapChanged(newMap.isValid() ? newMap : staticMapList->currentIndex()); lblMapList->setText(tr("Map:")); lblMapList->show(); + setMapNameLabel(m_curMap, !m_missingMap); + if(m_master) + { + staticMapList->show(); + } + else + { + teMapName->show(); + } mapFeatureSize->hide(); - staticMapList->show(); emit mapChanged(m_curMap); break; case MapModel::FortsMap: @@ -984,12 +1115,10 @@ { m_theme = selectedTheme = current.data(ThemeModel::ActualNameRole).toString(); m_themeID = current.row(); - QIcon icon = qVariantValue(current.data(Qt::DecorationRole)); - //QSize iconSize = icon.actualSize(QSize(65535, 65535)); - //btnTheme->setFixedHeight(64); - //btnTheme->setIconSize(iconSize); + QIcon icon = current.data(Qt::DecorationRole).value(); btnTheme->setIcon(icon); - btnTheme->setText(tr("Theme: %1").arg(current.data(Qt::DisplayRole).toString())); + QString themeLabel = tr("Theme: %1").arg(current.data(Qt::DisplayRole).toString()); + btnTheme->setText(themeLabel); updateThemeButtonSize(); } @@ -1008,24 +1137,31 @@ { QListView * mapList; - if (type == 0) mapList = staticMapList; - else if (type == 1) mapList = missionMapList; - else return; + if (type == 0) + { + mapList = staticMapList; + m_mapInfo.type = MapModel::StaticMap; + } + else if (type == 1) + { + mapList = missionMapList; + m_mapInfo.type = MapModel::MissionMap; + } + else + return; // Make sure it is a valid index if (!map.isValid()) { + // Make sure there's always a valid selection in the map list if (old.isValid()) { mapList->setCurrentIndex(old); mapList->scrollTo(old); } - else - { - m_mapInfo.type = MapModel::Invalid; - updatePreview(); - } - + m_mapInfo.type = MapModel::Invalid; + m_missingMap = true; + updatePreview(); return; } @@ -1035,6 +1171,11 @@ mapList->setCurrentIndex(map); mapList->scrollTo(map); } + if (m_missingMap) + { + m_missingMap = false; + updatePreview(); + } Q_ASSERT(map.data(Qt::UserRole + 1).canConvert()); // Houston, we have a problem. setMapInfo(map.data(Qt::UserRole + 1).value()); @@ -1046,9 +1187,9 @@ m_curMap = m_mapInfo.name; // the map has no pre-defined theme, so let's use the selected one - if (m_mapInfo.theme.isEmpty()) + if (m_mapInfo.theme.isNull() || m_mapInfo.theme.isEmpty()) { - if (!selectedTheme.isEmpty()) + if (!selectedTheme.isNull() && !selectedTheme.isEmpty()) { setTheme(selectedTheme); emit themeChanged(selectedTheme); @@ -1068,7 +1209,8 @@ void HWMapContainer::loadDrawing() { - QString fileName = QFileDialog::getOpenFileName(NULL, tr("Load drawn map"), ".", tr("Drawn Maps") + " (*.hwmap);;" + tr("All files") + " (*)"); + QString loadDir = QDir(cfgdir->absolutePath() + "/DrawnMaps").absolutePath(); + QString fileName = QFileDialog::getOpenFileName(this, tr("Load drawn map"), loadDir, tr("Drawn Maps") + " (*.hwmap);;" + tr("All files") + " (*)"); if(fileName.isEmpty()) return; @@ -1109,18 +1251,69 @@ foreach (QWidget *widget, m_childWidgets) widget->setEnabled(master); + + if(m_mapInfo.type == MapModel::StaticMap) + { + teMapName->setHidden(master); + staticMapList->setVisible(master); + } + else if(m_mapInfo.type == MapModel::MissionMap) + { + teMapName->setHidden(master); + missionMapList->setVisible(master); + } + + if(master) + { + // Room delegation cleanup if we get room control. + + if(m_missingMap) + { + // Reset map if we don't have the host's map + m_missingMap = false; + if(m_mapInfo.type == MapModel::MissionMap) + { + missionMapList->selectionModel()->setCurrentIndex(m_missionMapModel->index(0, 0), QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent); + } + else + { + if(m_mapInfo.type != MapModel::StaticMap) + { + changeMapType(MapModel::StaticMap); + } + staticMapList->selectionModel()->setCurrentIndex(m_staticMapModel->index(0, 0), QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent); + } + } + else + { + // Set random theme if we don't have it + QModelIndexList mdl = m_themeModel->match(m_themeModel->index(0), ThemeModel::ActualNameRole, m_theme); + if(!mdl.size()) + setRandomTheme(); + } + } + else + { + setMapNameLabel(m_curMap, true); + } } -void HWMapContainer::intSetIconlessTheme(const QString & name) +void HWMapContainer::setMissingTheme(const QString & name) { - if (name.isEmpty()) return; + if (name.isNull() || name.isEmpty()) return; m_theme = name; - btnTheme->setIcon(QIcon()); - btnTheme->setText(tr("Theme: %1").arg(name)); + QPixmap pixMissing = QPixmap(":/res/missingTheme@2x.png"); + QIcon iconMissing = QIcon(); + iconMissing.addPixmap(pixMissing, QIcon::Normal); + iconMissing.addPixmap(pixMissing, QIcon::Disabled); + btnTheme->setIcon(iconMissing); + // Question mark in front of theme name denotes it's missing + btnTheme->setText(tr("Theme: %1").arg("?" + name)); + updateThemeButtonSize(); } -void HWMapContainer::setupMissionMapsView() +void HWMapContainer::setupMissionMapsView(const QString & initialMap) { if(m_missionsViewSetup) return; m_missionsViewSetup = true; @@ -1133,10 +1326,13 @@ SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(missionMapChanged(const QModelIndex &, const QModelIndex &))); - missionSelectionModel->setCurrentIndex(m_missionMapModel->index(0, 0), QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent); + int m = 0; + if(!initialMap.isNull()) + m = m_missionMapModel->findMap(initialMap); + missionSelectionModel->setCurrentIndex(m_missionMapModel->index(m, 0), QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent); } -void HWMapContainer::setupStaticMapsView() +void HWMapContainer::setupStaticMapsView(const QString & initialMap) { if(m_staticViewSetup) return; m_staticViewSetup = true; @@ -1149,5 +1345,25 @@ SIGNAL(currentRowChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(staticMapChanged(const QModelIndex &, const QModelIndex &))); - staticSelectionModel->setCurrentIndex(m_staticMapModel->index(0, 0), QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent); + int m = 0; + if(!initialMap.isNull()) + m = m_staticMapModel->findMap(initialMap); + staticSelectionModel->setCurrentIndex(m_staticMapModel->index(m, 0), QItemSelectionModel::Clear | QItemSelectionModel::SelectCurrent); } + +// Call this function instead of setting the text of the map name label +// directly. +void HWMapContainer::setMapNameLabel(QString mapName, bool validMap) +{ + // Cut off insanely long names to be displayed + if(mapName.length() >= 90) + { + mapName.truncate(84); + mapName.append(" (...)"); + } + teMapName->setPlainText(mapName); + if(validMap) + teMapName->setStyleSheet("background-color: transparent;"); + else + teMapName->setStyleSheet("background-color: transparent; color: #b50000;"); +}