1# Copyright (C) 2016-2020 Damon Lynch <damonlynch@gmail.com> 2 3# This file is part of Rapid Photo Downloader. 4# 5# Rapid Photo Downloader is free software: you can redistribute it and/or 6# modify it under the terms of the GNU General Public License as published by 7# the Free Software Foundation, either version 3 of the License, or 8# (at your option) any later version. 9# 10# Rapid Photo Downloader is distributed in the hope that it will be useful, 11# but WITHOUT ANY WARRANTY; without even the implied warranty of 12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13# GNU General Public License for more details. 14# 15# You should have received a copy of the GNU General Public License 16# along with Rapid Photo Downloader. If not, 17# see <http://www.gnu.org/licenses/>. 18 19__author__ = 'Damon Lynch' 20__copyright__ = "Copyright 2016-2020, Damon Lynch" 21 22import math 23 24from PyQt5.QtCore import QSize, Qt 25from PyQt5.QtGui import ( 26 QFont, QIcon, QFontMetrics, QGuiApplication, QPainter, QPaintEvent, 27) 28from PyQt5.QtWidgets import (QPushButton, QSizePolicy) 29 30from raphodo.rotatedpushbutton import FlatButton 31 32 33class TopPushButton(QPushButton, FlatButton): 34 def __init__(self, text, parent, extra_top: int=0) -> None: 35 """ 36 37 :param text: text to display in the button 38 :param extra_top: extra spacing at the top of the widget 39 :param parent: parent widget 40 """ 41 42 super().__init__(text, parent) 43 self.rapidApp = parent 44 self.setCheckable(True) 45 self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) 46 47 font = self.font() # type: QFont 48 top_row_font_size = font.pointSize() + 8 49 self.top_row_icon_size = top_row_font_size + 10 50 font.setPointSize(top_row_font_size) 51 self.setFont(font) 52 53 font_height = QFontMetrics(font).height() 54 self.padding_side = math.ceil(font_height / 3.5) 55 padding_bottom = math.ceil(font_height / 5.6) 56 padding_top = padding_bottom + extra_top 57 58 self.non_elided_text = '' 59 60 padding = 'padding-left: {padding_side}px; padding-right: {padding_side}px; padding-top: ' \ 61 '{padding_top}px; padding-bottom: {padding_bottom}px;'.format( 62 padding_top=padding_top, padding_side=self.padding_side, 63 padding_bottom=padding_bottom 64 ) 65 self.setFlatStyle(self, darker_if_checked=False, padding=padding) 66 67 def text(self) -> str: 68 return self.non_elided_text 69 70 def setText(self, text: str) -> None: 71 self.non_elided_text = text 72 self.update() 73 74 def setIcon(self, icon: QIcon) -> None: 75 super().setIcon(icon) 76 self.setIconSize(QSize(self.top_row_icon_size, self.top_row_icon_size)) 77 78 def paintEvent(self, event: QPaintEvent): 79 """ 80 Override default rendering to elide button text if it is bigger than half the window 81 size 82 """ 83 84 painter = QPainter(self) 85 metrics = painter.fontMetrics() 86 right_element_widths = self.rapidApp.downloadButton.width() + self.rapidApp.menuButton.width() 87 window_width = self.rapidApp.width() 88 window_half = window_width / 2 89 if right_element_widths > window_half: 90 maximum_width = window_width - right_element_widths 91 else: 92 maximum_width = window_half 93 maximum_width -= self.padding_side - self.top_row_icon_size 94 95 # account for situations where we might have negative values, i.e., display some 96 # text at least 97 maximum_width = max(30, maximum_width) 98 99 usable_width = round(0.9 * maximum_width) 100 elided_text = metrics.elidedText(self.non_elided_text, Qt.ElideMiddle, usable_width) 101 super().setText(elided_text) 102 super().paintEvent(event) 103 104 105class DownloadButton(QPushButton): 106 """ 107 Button used to initiate downloads 108 """ 109 110 def __init__(self, text: str, parent) -> None: 111 super().__init__(text, parent) 112 113 self.rapidApp = parent 114 self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Maximum) 115 116 font_height = QFontMetrics(self.font()).tightBoundingRect( 117 _('Download 8 Photos and 10 Videos')).height() 118 padding = math.ceil(font_height * 1.7) 119 height = font_height // 2 * 6 120 radius = height // 2 121 122 palette = QGuiApplication.palette() 123 primaryColor = palette.highlight().color() 124 borderColor = primaryColor.darker(105) 125 hoverColor = palette.highlight().color().darker(106) 126 hoverBorderColor = hoverColor.darker(105) 127 primaryTextColor = palette.highlightedText().color() 128 129 disabledColor = palette.window().color().darker(120) 130 disabledBorderColor = disabledColor.darker(105) 131 disabledTextColor = palette.highlightedText().color() 132 133 # outline:none is used to remove the rectangle that appears on a 134 # button when the button has focus 135 # http://stackoverflow.com/questions/17280056/qt-css-decoration-on-focus 136 self.setStyleSheet(""" 137 QPushButton { 138 background-color: %(color)s; 139 outline: none; 140 padding-left: %(padding)dpx; 141 padding-right: %(padding)dpx; 142 border-radius: %(radius)dpx; 143 border: 1px solid %(borderColor)s; 144 height: %(height)dpx; 145 color: %(textcolor)s; 146 } 147 QPushButton:hover { 148 background-color: %(hoverColor)s; 149 border: 1px solid %(hoverBorderColor)s; 150 } 151 QPushButton:disabled { 152 background-color: %(disabledColor)s; 153 color: %(disabledTextColor)s; 154 border: 1px solid %(disabledBorderColor)s; 155 } 156 """ % dict( 157 color=primaryColor.name(), 158 padding=padding, 159 borderColor=borderColor.name(), 160 hoverColor=hoverColor.name(), 161 hoverBorderColor=hoverBorderColor.name(), 162 height=height, 163 radius=radius, 164 textcolor=primaryTextColor.name(), 165 disabledColor=disabledColor.name(), 166 disabledTextColor=disabledTextColor.name(), 167 disabledBorderColor=disabledBorderColor.name() 168 ) 169 ) 170 171 def setText(self, text: str) -> None: 172 super().setText(text) 173 self.rapidApp.sourceButton.updateGeometry() 174