/* * Copyright (C) 2006 George Staikos * Copyright (C) 2006 Dirk Mueller * Copyright (C) 2006 Zack Rusin * Copyright (C) 2006 Simon Hausmann * * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE COMPUTER, INC. OR * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include class InfoWidget :public QProgressBar { Q_OBJECT public: InfoWidget(QWidget *parent) : QProgressBar(parent), m_progress(0) { setMinimum(0); setMaximum(100); } QSize sizeHint() const { QSize size(100, 20); return size; } public slots: void startLoad() { setValue(m_progress); show(); } void changeLoad(int change) { m_progress = change; setValue(change); } void endLoad() { QTimer::singleShot(1000, this, SLOT(hide())); m_progress = 0; } protected: int m_progress; }; class HoverLabel : public QWidget { Q_OBJECT public: HoverLabel(QWidget *parent=0) : QWidget(parent), m_animating(false), m_percent(0) { m_timer.setInterval(1000/30); m_hideTimer.setInterval(500); m_hideTimer.setSingleShot(true); connect(&m_timer, SIGNAL(timeout()), this, SLOT(update())); connect(&m_hideTimer, SIGNAL(timeout()), this, SLOT(hide())); } public slots: void setHoverLink(const QString &link) { m_link = link; if (m_link.isEmpty()) { m_hideTimer.start(); } else { m_hideTimer.stop(); m_oldSize = m_newSize; m_newSize = sizeForFont(); resetAnimation(); updateSize(); show(); repaint(); } } QSize sizeForFont() const { QFont f = font(); QFontMetrics fm(f); return QSize(fm.width(m_link) + 10, fm.height() + 6); } QSize sizeHint() const { if (!m_animating) return sizeForFont(); else return (m_newSize.width() > m_oldSize.width()) ? m_newSize : m_oldSize; } void updateSize() { QRect r = geometry(); QSize newSize = sizeHint(); r = QRect(r.x(), r.y(), newSize.width(), newSize.height()); setGeometry(r); } void resetAnimation() { m_animating = true; m_percent = 0; if (!m_timer.isActive()) m_timer.start(); } protected: void paintEvent(QPaintEvent *e) { QPainter p(this); p.setClipRect(e->rect()); p.setPen(QPen(Qt::black, 1)); QLinearGradient gradient(rect().topLeft(), rect().bottomLeft()); gradient.setColorAt(0, QColor(255, 255, 255, 220)); gradient.setColorAt(1, QColor(193, 193, 193, 220)); p.setBrush(QBrush(gradient)); QSize size; { //draw a nicely rounded corner rectangle. to avoid unwanted // borders we move the coordinates outsize the our clip region size = interpolate(m_oldSize, m_newSize, m_percent); QRect r(-1, 0, size.width(), size.height()+2); const int roundness = 20; QPainterPath path; path.moveTo(r.x(), r.y()); path.lineTo(r.topRight().x()-roundness, r.topRight().y()); path.cubicTo(r.topRight().x(), r.topRight().y(), r.topRight().x(), r.topRight().y(), r.topRight().x(), r.topRight().y() + roundness); path.lineTo(r.bottomRight()); path.lineTo(r.bottomLeft()); path.closeSubpath(); p.setRenderHint(QPainter::Antialiasing); p.drawPath(path); } if (m_animating) { if (qFuzzyCompare(m_percent, 1)) { m_animating = false; m_percent = 0; m_timer.stop(); } else { m_percent += 0.1; if (m_percent >= 0.99) { m_percent = 1; } } } QString txt; QFontMetrics fm(fontMetrics()); txt = fm.elidedText(m_link, Qt::ElideRight, size.width()-5); p.drawText(5, height()-6, txt); } private: QSize interpolate(const QSize &src, const QSize &dst, qreal percent) { int widthDiff = int((dst.width() - src.width()) * percent); int heightDiff = int((dst.height() - src.height()) * percent); return QSize(src.width() + widthDiff, src.height() + heightDiff); } QString m_link; bool m_animating; QTimer m_timer; QTimer m_hideTimer; QSize m_oldSize; QSize m_newSize; qreal m_percent; }; class SearchEdit; class ClearButton : public QPushButton { Q_OBJECT public: ClearButton(QWidget *w) : QPushButton(w) { setMinimumSize(24, 24); setFixedSize(24, 24); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); } void paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); int height = parentWidget()->geometry().height(); int width = height; //parentWidget()->geometry().width(); painter.setRenderHint(QPainter::Antialiasing, true); painter.setPen(Qt::lightGray); painter.setBrush(isDown() ? QColor(140, 140, 190) : underMouse() ? QColor(220, 220, 255) : QColor(200, 200, 230) ); painter.drawEllipse(4, 4, width - 8, height - 8); painter.setPen(Qt::white); int border = 8; painter.drawLine(border, border, width - border, height - border); painter.drawLine(border, height - border, width - border, border); } }; class SearchEdit : public QLineEdit { Q_OBJECT public: SearchEdit(const QString &str, QWidget *parent = 0) : QLineEdit(str, parent) { setMinimumSize(QSize(400, 24)); setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); setStyleSheet(":enabled { padding-right: 27px }"); clearButton = new ClearButton(this); clearButton->setGeometry(QRect(geometry().right() - 27, geometry().top() - 2, geometry().right(), geometry().bottom())); clearButton->setVisible(true); #ifndef QT_NO_CURSOR clearButton->setCursor(Qt::ArrowCursor); #endif #ifndef QT_NO_TOOLTIP clearButton->setToolTip("Clear"); #endif connect(clearButton, SIGNAL(clicked()), this, SLOT(clear())); } ~SearchEdit() { } protected: virtual void paintEvent(QPaintEvent *e) { QLineEdit::paintEvent(e); if(text().isEmpty()) clearButton->setVisible(false); else clearButton->setVisible(true); } virtual void resizeEvent(QResizeEvent *) { clearButton->setParent(this); clearButton->setGeometry(QRect(width()-27, 0, 24, 24)); } virtual void moveEvent(QMoveEvent *) { clearButton->setParent(this); clearButton->setGeometry(QRect(width()-27, 1, 24, 24)); } QPushButton *clearButton; }; class WebPage : public QWebPage { public: inline WebPage(QWidget *parent) : QWebPage(parent) {} virtual QWebPage *createWindow(); }; class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(const QUrl &url = QUrl()) { page = new WebPage(this); InfoWidget *info = new InfoWidget(page); info->setGeometry(20, 20, info->sizeHint().width(), info->sizeHint().height()); connect(page, SIGNAL(loadStarted(QWebFrame*)), info, SLOT(startLoad())); connect(page, SIGNAL(loadProgressChanged(int)), info, SLOT(changeLoad(int))); connect(page, SIGNAL(loadFinished(QWebFrame*)), info, SLOT(endLoad())); connect(page, SIGNAL(loadFinished(QWebFrame*)), this, SLOT(loadFinished())); connect(page, SIGNAL(titleChanged(const QString&)), this, SLOT(setWindowTitle(const QString&))); connect(page, SIGNAL(hoveringOverLink(const QString&, const QString&)), this, SLOT(showLinkHover(const QString&, const QString&))); setCentralWidget(page); QToolBar *bar = addToolBar("Navigation"); urlEdit = new SearchEdit(url.toString()); urlEdit->setSizePolicy(QSizePolicy::Expanding, urlEdit->sizePolicy().verticalPolicy()); connect(urlEdit, SIGNAL(returnPressed()), SLOT(changeLocation())); bar->addAction(page->action(QWebPage::GoBack)); bar->addAction(page->action(QWebPage::Stop)); bar->addAction(page->action(QWebPage::GoForward)); bar->addSeparator(); bar->addAction(page->action(QWebPage::Cut)); bar->addAction(page->action(QWebPage::Copy)); bar->addAction(page->action(QWebPage::Paste)); bar->addSeparator(); bar->addAction(page->action(QWebPage::Undo)); bar->addAction(page->action(QWebPage::Redo)); addToolBarBreak(); bar = addToolBar("Location"); bar->addWidget(new QLabel(tr("Location:"))); bar->addWidget(urlEdit); hoverLabel = new HoverLabel(this); hoverLabel->hide(); if (url.isValid()) page->open(url); info->raise(); } inline QWebPage *webPage() const { return page; } protected slots: void changeLocation() { QUrl url(urlEdit->text()); page->open(url); } void loadFinished() { urlEdit->setText(page->url().toString()); } void showLinkHover(const QString &link, const QString &toolTip) { //statusBar()->showMessage(link); hoverLabel->setHoverLink(link); #ifndef QT_NO_TOOLTIP if (!toolTip.isEmpty()) QToolTip::showText(QCursor::pos(), toolTip); #endif } protected: void resizeEvent(QResizeEvent *) { QSize hoverSize = hoverLabel->sizeHint(); hoverLabel->setGeometry(0, height()-hoverSize.height(), 300, hoverSize.height()); } private: QWebPage *page; QLineEdit *urlEdit; HoverLabel *hoverLabel; }; QWebPage *WebPage::createWindow() { MainWindow *mw = new MainWindow; return mw->webPage(); } #include "main.moc" int main(int argc, char **argv) { QString url = QString("%1/%2").arg(QDir::homePath()).arg(QLatin1String("index.html")); QApplication app(argc, argv); QWebSettings::defaultSettings()->setAttribute(QWebSettings::PluginsEnabled); QWebSettings::defaultSettings()->setAttribute(QWebSettings::DeveloperExtrasEnabled); const QStringList args = app.arguments(); if (args.count() > 1) url = args.at(1); QUrl qurl(url); if (qurl.scheme().isEmpty()) qurl = QUrl::fromLocalFile(QFileInfo(url).absoluteFilePath()); MainWindow window(qurl); window.show(); return app.exec(); }