diff --git a/spyder/plugins/application/widgets/appeal_page/assets/appeal.css b/spyder/plugins/application/widgets/appeal_page/assets/appeal.css new file mode 100644 index 00000000000..aafdfdb789b --- /dev/null +++ b/spyder/plugins/application/widgets/appeal_page/assets/appeal.css @@ -0,0 +1 @@ +*,:before,:after{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}:before,:after{--tw-content: ""}html,:host{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;-o-tab-size:4;tab-size:4;font-feature-settings:normal;font-variation-settings:normal;-webkit-tap-highlight-color:transparent}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace;font-feature-settings:normal;font-variation-settings:normal;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-feature-settings:inherit;font-variation-settings:inherit;font-size:100%;font-weight:inherit;line-height:inherit;letter-spacing:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}button,input:where([type=button]),input:where([type=reset]),input:where([type=submit]){-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dl,dd,h1,h2,h3,h4,h5,h6,hr,figure,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}ol,ul,menu{list-style:none;margin:0;padding:0}dialog{padding:0}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{opacity:1;color:#9ca3af}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}button,[role=button]{cursor:pointer}:disabled{cursor:default}img,svg,video,canvas,audio,iframe,embed,object{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*,:before,:after{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }::backdrop{--tw-border-spacing-x: 0;--tw-border-spacing-y: 0;--tw-translate-x: 0;--tw-translate-y: 0;--tw-rotate: 0;--tw-skew-x: 0;--tw-skew-y: 0;--tw-scale-x: 1;--tw-scale-y: 1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness: proximity;--tw-gradient-from-position: ;--tw-gradient-via-position: ;--tw-gradient-to-position: ;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width: 0px;--tw-ring-offset-color: #fff;--tw-ring-color: rgb(59 130 246 / .5);--tw-ring-offset-shadow: 0 0 #0000;--tw-ring-shadow: 0 0 #0000;--tw-shadow: 0 0 #0000;--tw-shadow-colored: 0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: ;--tw-contain-size: ;--tw-contain-layout: ;--tw-contain-paint: ;--tw-contain-style: }.-mx-5{margin-left:-1.25rem;margin-right:-1.25rem}.flex{display:flex}.grid{display:grid}.w-full{width:100%}.max-w-screen-sm{max-width:640px}.items-center{align-items:center}.justify-center{justify-content:center}.bg-slate-50{--tw-bg-opacity: 1;background-color:rgb(248 250 252 / var(--tw-bg-opacity))}.p-10{padding:2.5rem}:root{--dark: #062647;--med: #26486b;--light: #dfe1e2;--highlight: #8c0000;--action: #ac0000;line-height:1.6;font-weight:400;color:var(--dark);font-synthesis:none;text-rendering:optimizeLegibility;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}@font-face{font-family:""}a{font-weight:500;color:var(--action);text-decoration:inherit}a:hover{color:var(--action)}body{margin:0;min-height:100vh}h1{font-size:3.2em;line-height:1.1}p{text-align:left}.center{text-align:center}.bold{font-weight:900}.underline{-webkit-text-decoration:underline wavy var(--highlight);text-decoration:underline wavy var(--highlight);text-underline-offset:.3em}.logo{width:250px;padding:0 30px 30px}.logo:hover{scale:1.2}.button{width:240px;height:100px;border-radius:8px;padding:15px;margin:20px auto;background-color:var(--highlight);cursor:pointer;box-shadow:0 0 5px #00000040;display:flex;align-items:center;justify-content:center}.button:hover{background-color:var(--action)}.button img{width:100%;height:auto}.heart-container{position:absolute;top:28%;right:25%;display:grid;align-items:center;justify-content:center;z-index:-1}.heart{background-color:var(--highlight);position:relative;transform:rotate(45deg);display:block}.heart:before,.heart:after{content:"";position:absolute;top:0;right:0;bottom:0;left:0;background-color:inherit;border-radius:50%;display:block}.heart:before{transform:translateY(-50%)}.heart:after{transform:translate(-50%)}.heart-beat{height:6vw;width:6vw;animation:beat 1.4s infinite linear}.grid-center{display:grid;position:relative;align-items:center;justify-content:center;z-index:2}.content{padding-top:20px}.content p{text-align:center;margin-bottom:0}@keyframes beat{0%{transform:rotate(45deg) scale(1)}25%{transform:rotate(45deg) scale(1)}30%{transform:rotate(45deg) scale(1.4)}50%{transform:rotate(45deg) scale(1.2)}70%{transform:rotate(45deg) scale(1.4)}to{transform:rotate(45deg) scale(1)}} diff --git a/spyder/plugins/application/widgets/appeal_page/header_donations.svg b/spyder/plugins/application/widgets/appeal_page/header_donations.svg new file mode 100644 index 00000000000..ddb9f5b1c01 --- /dev/null +++ b/spyder/plugins/application/widgets/appeal_page/header_donations.svg @@ -0,0 +1,199 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/spyder/plugins/application/widgets/appeal_page/icon_donations.svg b/spyder/plugins/application/widgets/appeal_page/icon_donations.svg new file mode 100644 index 00000000000..90360209f51 --- /dev/null +++ b/spyder/plugins/application/widgets/appeal_page/icon_donations.svg @@ -0,0 +1,95 @@ + + + + + + + + + + + + + + + + + + diff --git a/spyder/plugins/application/widgets/appeal_page/index.html b/spyder/plugins/application/widgets/appeal_page/index.html new file mode 100644 index 00000000000..434a3b4d630 --- /dev/null +++ b/spyder/plugins/application/widgets/appeal_page/index.html @@ -0,0 +1,52 @@ + + + + + + Donate today! + + + + +
+ + + + +
+ Help Keep Spyder Strong! +
+ +
+
+ + + + +
+

Spyder is a fully independent project, unaffiliated with Anaconda or + other companies. As it’s developed by and for + you, our beloved community, we need + your support to help keep it moving forward! + Your donation goes directly to funding Spyder development through NumFOCUS, a recognized charitable + organization.

+
+
+ + diff --git a/spyder/plugins/application/widgets/appeal_page/spyder-logo.svg b/spyder/plugins/application/widgets/appeal_page/spyder-logo.svg new file mode 100644 index 00000000000..43957b8dd3c --- /dev/null +++ b/spyder/plugins/application/widgets/appeal_page/spyder-logo.svg @@ -0,0 +1,4 @@ + + + + diff --git a/spyder/plugins/application/widgets/status.py b/spyder/plugins/application/widgets/status.py index 11b7b7f1183..182dae84249 100644 --- a/spyder/plugins/application/widgets/status.py +++ b/spyder/plugins/application/widgets/status.py @@ -7,11 +7,72 @@ """Status bar widgets.""" # Standard library imports +import os.path as osp import datetime +# Third-party imports +from qtpy.QtCore import Qt, QUrl +from qtpy.QtWidgets import QDialog, QVBoxLayout + # Local imports +from spyder.api.fonts import SpyderFontType, SpyderFontsMixin from spyder.api.widgets.status import BaseTimerStatus from spyder.api.translations import _ +from spyder.config.base import get_module_source_path +from spyder.utils.icon_manager import ima +from spyder.utils.qthelpers import start_file +from spyder.utils.stylesheet import MAC, WIN +from spyder.widgets.browser import WebView + + +class InAppAppealDialog(QDialog, SpyderFontsMixin): + + CONF_SECTION = "main" + WIDTH = 530 if WIN else 530 + HEIGHT = 665 if WIN else 665 + + def __init__(self, parent=None): + super().__init__(parent) + + # Attributes + self.setWindowFlags( + self.windowFlags() & ~Qt.WindowContextHelpButtonHint + ) + self.setFixedWidth(self.WIDTH) + self.setFixedHeight(self.HEIGHT) + self.setWindowTitle(_("Help Spyder")) + self.setWindowIcon(ima.icon("inapp_appeal")) + + # Path to the appeal page + appeal_page_path = osp.join( + get_module_source_path("spyder.plugins.application.widgets"), + "appeal_page", + "index.html", + ) + + # Create webview to render the appeal message + webview = WebView(self, handle_links=True) + + # Set font used in the view + app_font = self.get_font(SpyderFontType.Interface) + webview.set_font(app_font, size_delta=2) + + # Load page + webview.load(QUrl.fromLocalFile(appeal_page_path)) + + # Open links in the user browser and not inside webview + webview.page().linkClicked.connect(self._handle_link_clicks) + + # Layout + layout = QVBoxLayout() + layout.setContentsMargins(0, 0, 0, 0) + layout.addWidget(webview) + self.setLayout(layout) + + def _handle_link_clicks(self, url): + url = str(url.toString()) + if url.startswith('http'): + start_file(url) class InAppAppealStatus(BaseTimerStatus): @@ -27,6 +88,7 @@ def __init__(self, parent=None): super().__init__(parent) self._is_shown = False + self._appeal_dialog = None # We don't need to show a label for this widget self.label_value.setVisible(False) @@ -34,6 +96,21 @@ def __init__(self, parent=None): # Update status every hour self.set_interval(60 * 60 * 1000) + # Show appeal on click + self.sig_clicked.connect(self._on_click) + + # ---- Private API + # ------------------------------------------------------------------------- + def _on_click(self): + """Handle widget clicks.""" + if self._appeal_dialog is None: + self._appeal_dialog = InAppAppealDialog(self) + + if self._appeal_dialog.isVisible(): + self._appeal_dialog.hide() + else: + self._appeal_dialog.show() + # ---- StatusBarWidget API # ------------------------------------------------------------------------- def get_icon(self):