[prev in list] [next in list] [prev in thread] [next in thread] 

List:       kde-games-devel
Subject:    Re: [Kde-games-devel] Palapeli preview window survey
From:       Johannes Loehnert <loehnert.kde () gmx ! de>
Date:       2010-08-14 9:26:43
Message-ID: 201008141126.44288.loehnert.kde () gmx ! de
[Download RAW message or body]

[Attachment #2 (multipart/signed)]

[Attachment #4 (multipart/mixed)]


Hello again,

so here is the solution I came up with originally. I had similar thoughts as 
you both and made the preview a separate tool window. 
 * Of course it can be moved and resized ad libitum.
 * It is always on top
 * Hovering the mouse over the window zooms the image.¹

What's different to your proposals:
 * not on by default; however if it was open on close, it will be there on 
restart.
 * no close button (but that's trivial)
 
I'm curious about your opinions. :-)
 
Best regards,
 Johannes

¹fine print: only tested with activation-follows-mouse policy

["puzzlepreview.patch" (text/x-patch)]

Index: src/palapeli-puzzletableui.rc
===================================================================
--- src/palapeli-puzzletableui.rc	(Revision 1163530)
+++ src/palapeli-puzzletableui.rc	(Arbeitskopie)
@@ -7,5 +7,6 @@
 	<ToolBar name="puzzleTableToolbar">
 		<text>Puzzle table toolbar</text>
 		<Action name="game_restart"/>
+		<Action name="toggle_preview"/>
 	</ToolBar>
 </gui>
Index: src/palapeli.kcfg
===================================================================
--- src/palapeli.kcfg	(Revision 1163530)
+++ src/palapeli.kcfg	(Arbeitskopie)
@@ -29,4 +29,14 @@
 			<default>true</default>
 		</entry>
 	</group>
+	<group name="PuzzlePreview">
+		<entry name="PuzzlePreviewVisible" type="Bool">
+			<label>Wether the preview window was switched on last time Palapeli was \
run.</label> +			<default>false</default>
+		</entry>
+		<entry name="PuzzlePreviewGeometry" type="Rect">
+			<label>Last position and size of puzzle preview.</label>
+			<default>QRect(-1, -1, 320, 240)</default>
+		</entry>
+	</group>
 </kcfg>
Index: src/engine/puzzlepreview.h
===================================================================
--- src/engine/puzzlepreview.h	(Revision 0)
+++ src/engine/puzzlepreview.h	(Revision 0)
@@ -0,0 +1,69 @@
+/***************************************************************************
+ *   Copyright 2010 Johannes Loehnert <loehnert.kde@gmx.de>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+***************************************************************************/
+
+#ifndef PALAPELI_PUZZLEPREVIEW_H
+#define PALAPELI_PUZZLEPREVIEW_H
+
+#include <QGraphicsView>
+#include <QModelIndex>
+#include <QPointer>
+#include <QTimer>
+
+
+namespace Palapeli
+{
+	class Puzzle;
+	class PuzzlePreview : public QGraphicsView
+	{
+		Q_OBJECT
+		public:
+			PuzzlePreview();
+			
+			void setImage(const QImage &image);
+			void loadImageFrom(const QModelIndex &index);
+			
+		public Q_SLOTS:
+			// toggles visibility state AND updates config with the new state.
+			void toggleVisible();
+
+		protected:
+			virtual void mouseMoveEvent(QMouseEvent* event);
+			virtual void enterEvent(QEvent* event);
+			virtual void leaveEvent(QEvent* event);
+			virtual void resizeEvent(QResizeEvent* event);
+			virtual void moveEvent(QMoveEvent *event);
+			void updateViewport();
+			void loadImageFromInternal(Palapeli::Puzzle *puzzle);
+			
+		private Q_SLOTS:
+			void writeConfigIfGeometryChanged();
+			
+		private:
+			
+			QPointer<Palapeli::Puzzle> m_puzzle;
+			// used to save geometry after move/resize, to avoid writing config file each \
time the cursor moves a pixel. +			QTimer* m_settingsSaveTimer;
+			bool m_geometryChanged;
+
+			qreal m_hoverZoom;
+			bool m_isZoomed;
+			QPoint m_mousePos;
+	};
+}
+
+#endif // PALAPELI_PUZZLEPREVIEW_H
Index: src/engine/puzzlepreview.cpp
===================================================================
--- src/engine/puzzlepreview.cpp	(Revision 0)
+++ src/engine/puzzlepreview.cpp	(Revision 0)
@@ -0,0 +1,185 @@
+/***************************************************************************
+ *   Copyright 2010 Johannes Loehnert <loehnert.kde@gmx.de>
+ *
+ *   This program is free software; you can redistribute it and/or
+ *   modify it under the terms of the GNU General Public
+ *   License as published by the Free Software Foundation; either
+ *   version 2 of the License, or (at your option) any later version.
+ *
+ *   This program is distributed in the hope that it will be useful,
+ *   but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *   GNU General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the Free Software
+ *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+***************************************************************************/
+
+#include "puzzlepreview.h"
+#include "../file-io/collection.h"
+#include "../file-io/puzzle.h"
+#include "settings.h"
+
+#include <cmath>
+#include <QMouseEvent>
+#include <QPainter>
+#include <QDebug>
+#include <KLocalizedString>
+
+Palapeli::PuzzlePreview::PuzzlePreview()
+{
+	m_settingsSaveTimer = new QTimer(this);
+	connect(m_settingsSaveTimer, SIGNAL(timeout()), this, \
SLOT(writeConfigIfGeometryChanged())); +	m_geometryChanged = false;
+	
+	m_hoverZoom = 1.0;
+	m_isZoomed = false;
+	m_mousePos = QPoint();
+	
+	setScene(new QGraphicsScene());
+	setWindowTitle(i18nc("tool window title", "Preview of assembled puzzle"));
+	setWindowFlags(Qt::Tool | Qt::CustomizeWindowHint | Qt::WindowTitleHint | \
Qt::WindowStaysOnTopHint); +	setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+	setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
+	setRenderHint(QPainter::SmoothPixmapTransform);
+	scene()->addText(i18nc("text in preview window", "No puzzle is loaded."));
+	setSceneRect(scene()->itemsBoundingRect());
+	
+	// read size and position settings
+	QRect geometry = Settings::puzzlePreviewGeometry();
+	resize(geometry.size());
+	
+	// default (-1/-1) toprect: don't change position
+	if (geometry.left() >= 0 && geometry.top() >= 0)
+		move(geometry.topLeft());
+	
+	m_settingsSaveTimer->start(500);
+	hide();
+	updateViewport();
+}
+
+void Palapeli::PuzzlePreview::setImage(const QImage &image)
+{
+	scene()->clear();
+	scene()->addPixmap(QPixmap::fromImage(image));
+	setSceneRect(image.rect());
+	updateViewport();
+}
+
+void Palapeli::PuzzlePreview::loadImageFrom(const QModelIndex &index)
+{
+	QObject* puzzlePayload = \
index.data(Palapeli::Collection::PuzzleObjectRole).value<QObject*>(); \
+	Palapeli::Puzzle* puzzle = qobject_cast<Palapeli::Puzzle*>(puzzlePayload); +	if \
(puzzle && m_puzzle != puzzle) +	{
+		m_puzzle = puzzle;
+		loadImageFromInternal(puzzle);
+	}
+}
+
+void Palapeli::PuzzlePreview::loadImageFromInternal(Palapeli::Puzzle *puzzle)
+{
+	// metadata was assumedly loaded by Palapeli::Scene.
+	// FIXME: possible race condition? in case of doubt, how to wait for metadata?
+	// (would have to catch Scene::m_metadataLoader finished() --> bad design!)
+	// (or, use a timer and look every x ms if we can proceed)
+	if (puzzle->metadata())
+	{
+		setImage(puzzle->metadata()->image);
+		
+		// set hover zoom so that 3x3 pieces would be visible on a square grid.
+		m_hoverZoom = sqrt(puzzle->metadata()->pieceCount)/3.0;
+		if (m_hoverZoom < 1)
+			m_hoverZoom = 1;
+	}
+	else
+	{
+		qWarning() << "cannot set image: metadata not loaded";
+	}
+}
+
+void Palapeli::PuzzlePreview::toggleVisible()
+{
+	setVisible(!isVisible());
+	Settings::setPuzzlePreviewVisible(isVisible());
+	Settings::self()->writeConfig();
+}
+
+void Palapeli::PuzzlePreview::mouseMoveEvent(QMouseEvent* event)
+{
+	m_mousePos = event->pos();
+	updateViewport();
+	QGraphicsView::mouseMoveEvent(event);
+}
+
+void Palapeli::PuzzlePreview::enterEvent(QEvent* event)
+{
+	setMouseTracking(true);
+	m_isZoomed = true;
+	m_mousePos = QPoint();
+	// wait with update for first mouseMoveEvent
+	QGraphicsView::enterEvent(event);
+}
+
+void Palapeli::PuzzlePreview::leaveEvent(QEvent* event)
+{
+	setMouseTracking(false);
+	m_isZoomed = false;
+	updateViewport();
+	QGraphicsView::leaveEvent(event);
+}
+
+void Palapeli::PuzzlePreview::resizeEvent(QResizeEvent* event)
+{
+	updateViewport();
+	m_geometryChanged = true;
+	QGraphicsView::resizeEvent(event);
+}
+
+void Palapeli::PuzzlePreview::moveEvent(QMoveEvent* event)
+{
+	m_geometryChanged = true;
+	QGraphicsView::moveEvent(event);
+}
+
+void Palapeli::PuzzlePreview::writeConfigIfGeometryChanged()
+{
+	if (!m_geometryChanged) return;
+	
+	qDebug() << "puzzle preview: saving changed geometry";
+	
+	// move() includes window frame, resize() doesn't :-/
+	Settings::setPuzzlePreviewGeometry(QRect(frameGeometry().topLeft(), size()));
+	Settings::self()->writeConfig();
+	m_geometryChanged = false;
+}
+	
+
+void Palapeli::PuzzlePreview::updateViewport()
+{
+	qreal zoom;
+	// calculate zoom for fit-in-window
+	zoom = width() / sceneRect().width();
+	if (zoom > height() / sceneRect().height())
+		zoom = height() / sceneRect().height();
+	
+	if (m_isZoomed)
+		zoom *= m_hoverZoom;
+	
+	// do not enlarge
+	if (zoom>1)
+		zoom = 1;
+	
+	resetTransform();
+	scale(zoom, zoom);
+	
+	if (m_isZoomed)
+	{
+		// focus moves with cursor position
+		QPointF pos = m_mousePos;
+		pos.rx() *= sceneRect().width() / width();
+		pos.ry() *= sceneRect().height() / height();
+		centerOn(pos);
+	}
+}
\ No newline at end of file
Index: src/window/puzzletablewidget.cpp
===================================================================
--- src/window/puzzletablewidget.cpp	(Revision 1163530)
+++ src/window/puzzletablewidget.cpp	(Arbeitskopie)
@@ -18,6 +18,7 @@
 
 #include "puzzletablewidget.h"
 #include "loadingwidget.h"
+#include "../engine/puzzlepreview.h"
 #include "../engine/scene.h"
 #include "../engine/view.h"
 #include "../engine/zoomwidget.h"
@@ -56,12 +57,17 @@
 	, m_view(new Palapeli::View)
 	, m_progressBar(new Palapeli::TextProgressBar(this))
 	, m_zoomWidget(new Palapeli::ZoomWidget(this))
+	, m_puzzlePreview(new Palapeli::PuzzlePreview())
 {
 	//setup actions
 	KAction* restartPuzzleAct = new KAction(KIcon("view-refresh"), i18n("&Restart \
puzzle..."), 0);  restartPuzzleAct->setToolTip(i18n("Delete the saved progress"));
 	actionCollection()->addAction("game_restart", restartPuzzleAct);
 	connect(restartPuzzleAct, SIGNAL(triggered()), m_view->scene(), \
SLOT(restartPuzzle())); +	KAction* togglePreviewAct = new \
KAction(KIcon("view-preview"), i18n("Toggle image &preview"), 0); \
+	togglePreviewAct->setToolTip(i18n("Shows or hides the image of the assembled \
puzzle")); +	actionCollection()->addAction("toggle_preview", togglePreviewAct);
+	connect(togglePreviewAct, SIGNAL(triggered()), m_puzzlePreview, \
SLOT(toggleVisible()));  setupGUI();
 	//setup progress bar
 	m_progressBar->setText(i18n("No puzzle loaded"));
@@ -95,6 +101,11 @@
 	return m_view;
 }
 
+Palapeli::PuzzlePreview* Palapeli::PuzzleTableWidget::puzzlePreview() const
+{
+	return m_puzzlePreview;
+}
+
 void Palapeli::PuzzleTableWidget::showStatusBar(bool visible)
 {
 	//apply setting
Index: src/window/puzzletablewidget.h
===================================================================
--- src/window/puzzletablewidget.h	(Revision 1163530)
+++ src/window/puzzletablewidget.h	(Arbeitskopie)
@@ -28,6 +28,7 @@
 	class LoadingWidget;
 	class TextProgressBar;
 	class View;
+	class PuzzlePreview;
 	class ZoomWidget;
 
 	class PuzzleTableWidget : public Palapeli::TabWindow
@@ -37,6 +38,7 @@
 			PuzzleTableWidget();
 
 			Palapeli::View* view() const;
+			Palapeli::PuzzlePreview* puzzlePreview() const;
 		public Q_SLOTS:
 			void reportProgress(int pieceCount, int partCount);
 			void showStatusBar(bool visible);
@@ -49,6 +51,7 @@
 			Palapeli::View* m_view;
 			Palapeli::TextProgressBar* m_progressBar;
 			Palapeli::ZoomWidget* m_zoomWidget;
+			Palapeli::PuzzlePreview* m_puzzlePreview;
 	};
 }
 
Index: src/window/mainwindow.cpp
===================================================================
--- src/window/mainwindow.cpp	(Revision 1163530)
+++ src/window/mainwindow.cpp	(Arbeitskopie)
@@ -19,6 +19,7 @@
 #include "mainwindow.h"
 #include "../config/configdialog.h"
 #include "../creator/puzzlecreator.h"
+#include "../engine/puzzlepreview.h"
 #include "../engine/scene.h"
 #include "../engine/view.h"
 #include "collectionwidget.h"
@@ -158,6 +159,8 @@
 void Palapeli::MainWindow::loadPuzzle(const QModelIndex& index)
 {
 	m_puzzleTable->view()->scene()->loadPuzzle(index);
+	m_puzzleTable->puzzlePreview()->loadImageFrom(index);
+	m_puzzleTable->puzzlePreview()->setVisible(Settings::puzzlePreviewVisible());
 	m_centralWidget->setTabEnabled(m_centralWidget->indexOf(m_puzzleTable), true);
 	m_centralWidget->setCurrentWidget(m_puzzleTable);
 	setCaption(index.data(Qt::DisplayRole).toString());
Index: src/CMakeLists.txt
===================================================================
--- src/CMakeLists.txt	(Revision 1163530)
+++ src/CMakeLists.txt	(Arbeitskopie)
@@ -19,6 +19,7 @@
 	engine/mergegroup.cpp
 	engine/piece.cpp
 	engine/piecevisuals.cpp
+	engine/puzzlepreview.cpp
 	engine/scene.cpp
 	engine/texturehelper.cpp
 	engine/trigger.cpp


["signature.asc" (application/pgp-signature)]

_______________________________________________
kde-games-devel mailing list
kde-games-devel@kde.org
https://mail.kde.org/mailman/listinfo/kde-games-devel


[prev in list] [next in list] [prev in thread] [next in thread] 

Configure | About | News | Add a list | Sponsored by KoreLogic