Browse Source

kivy: remove context menus, cleanup unused files

dependabot/pip/contrib/deterministic-build/ecdsa-0.13.3
ThomasV 6 years ago
parent
commit
e9c32bad19
  1. 5
      electrum/gui/kivy/main.kv
  2. 57
      electrum/gui/kivy/main_window.py
  3. 58
      electrum/gui/kivy/uix/context_menu.py
  4. 111
      electrum/gui/kivy/uix/dialogs/addresses.py
  5. 169
      electrum/gui/kivy/uix/dialogs/invoices.py
  6. 155
      electrum/gui/kivy/uix/dialogs/lightning_channels.py
  7. 65
      electrum/gui/kivy/uix/dialogs/lightning_invoices.py
  8. 33
      electrum/gui/kivy/uix/dialogs/request_dialog.py
  9. 16
      electrum/gui/kivy/uix/dialogs/tx_dialog.py
  10. 53
      electrum/gui/kivy/uix/screens.py
  11. 89
      electrum/gui/kivy/uix/ui_screens/invoice.kv

5
electrum/gui/kivy/main.kv

@ -231,15 +231,14 @@
size: self.size size: self.size
pos: self.pos pos: self.pos
<CardItem@ToggleButtonBehavior+BoxLayout> <CardItem@ButtonBehavior+BoxLayout>
size_hint: 1, None size_hint: 1, None
height: '65dp' height: '65dp'
group: 'requests' group: 'requests'
padding: dp(12) padding: dp(12)
spacing: dp(5) spacing: dp(5)
screen: None screen: None
on_state: on_release: self.screen.show_item(args[0])
self.screen.show_menu(args[0]) if self.state == 'down' else self.screen.hide_menu()
canvas.before: canvas.before:
Color: Color:
rgba: (0.192, .498, 0.745, 1) if self.state == 'down' else (0.15, 0.15, 0.17, 1) rgba: (0.192, .498, 0.745, 1) if self.state == 'down' else (0.15, 0.15, 0.17, 1)

57
electrum/gui/kivy/main_window.py

@ -418,41 +418,6 @@ class ElectrumWindow(App):
self.request_popup.set_status(status) self.request_popup.set_status(status)
self.request_popup.open() self.request_popup.open()
def show_pr_details(self, req, status, is_invoice):
from electrum.util import format_time
requestor = req.get('requestor')
exp = req.get('exp')
memo = req.get('memo')
amount = req.get('amount')
fund = req.get('fund')
popup = Builder.load_file('electrum/gui/kivy/uix/ui_screens/invoice.kv')
popup.is_invoice = is_invoice
popup.amount = amount
popup.requestor = requestor if is_invoice else req.get('address')
popup.exp = format_time(exp) if exp else ''
popup.description = memo if memo else ''
popup.signature = req.get('signature', '')
popup.status = status
popup.fund = fund if fund else 0
txid = req.get('txid')
popup.tx_hash = txid or ''
popup.on_open = lambda: popup.ids.output_list.update(req.get('outputs', []))
popup.export = self.export_private_keys
popup.open()
def show_addr_details(self, req, status):
from electrum.util import format_time
fund = req.get('fund')
isaddr = 'y'
popup = Builder.load_file('electrum/gui/kivy/uix/ui_screens/invoice.kv')
popup.isaddr = isaddr
popup.is_invoice = False
popup.status = status
popup.requestor = req.get('address')
popup.fund = fund if fund else 0
popup.export = self.export_private_keys
popup.open()
def qr_dialog(self, title, data, show_text=False, text_for_clipboard=None): def qr_dialog(self, title, data, show_text=False, text_for_clipboard=None):
from .uix.dialogs.qr_dialog import QRDialog from .uix.dialogs.qr_dialog import QRDialog
def on_qr_failure(): def on_qr_failure():
@ -1035,28 +1000,6 @@ class ElectrumWindow(App):
popup = AmountDialog(show_max, amount, cb) popup = AmountDialog(show_max, amount, cb)
popup.open() popup.open()
def lightning_invoices_dialog(self, cb):
from .uix.dialogs.lightning_invoices import LightningInvoicesDialog
report = self.wallet.lnworker._list_invoices()
if not report['unsettled']:
self.show_info(_('No unsettled invoices. Type in an amount to generate a new one.'))
return
popup = LightningInvoicesDialog(report, cb)
popup.open()
def invoices_dialog(self, screen):
from .uix.dialogs.invoices import InvoicesDialog
if len(self.wallet.invoices.sorted_list()) == 0:
self.show_info(' '.join([
_('No saved invoices.'),
_('Signed invoices are saved automatically when you scan them.'),
_('You may also save unsigned requests or contact addresses using the save button.')
]))
return
popup = InvoicesDialog(self, screen, None)
popup.update()
popup.open()
def addresses_dialog(self): def addresses_dialog(self):
from .uix.dialogs.addresses import AddressesDialog from .uix.dialogs.addresses import AddressesDialog
if self._addresses_dialog is None: if self._addresses_dialog is None:

58
electrum/gui/kivy/uix/context_menu.py

@ -1,58 +0,0 @@
#!python
#!/usr/bin/env python
from kivy.app import App
from kivy.uix.bubble import Bubble
from kivy.animation import Animation
from kivy.uix.floatlayout import FloatLayout
from kivy.lang import Builder
from kivy.factory import Factory
from kivy.clock import Clock
from electrum.gui.kivy.i18n import _
Builder.load_string('''
<MenuItem@Button>
background_normal: ''
background_color: (0.192, .498, 0.745, 1)
height: '48dp'
size_hint: 1, None
<ContextMenu>
size_hint: 1, None
height: '60dp'
pos: (0, 0)
show_arrow: False
arrow_pos: 'top_mid'
padding: 0
orientation: 'horizontal'
background_color: (0.1, 0.1, 0.1, 1)
background_image: ''
BoxLayout:
size_hint: 1, 1
height: '54dp'
padding: '0dp', '0dp'
spacing: '3dp'
orientation: 'horizontal'
id: buttons
''')
class MenuItem(Factory.Button):
pass
class ContextMenu(Bubble):
def __init__(self, obj, action_list):
Bubble.__init__(self)
self.obj = obj
for k, v in action_list:
l = MenuItem()
l.text = _(k)
def func(f=v):
Clock.schedule_once(lambda dt: f(obj), 0.15)
l.on_release = func
self.ids.buttons.add_widget(l)
def hide(self):
if self.parent:
self.parent.hide_menu()

111
electrum/gui/kivy/uix/dialogs/addresses.py

@ -3,6 +3,9 @@ from kivy.factory import Factory
from kivy.properties import ObjectProperty from kivy.properties import ObjectProperty
from kivy.lang import Builder from kivy.lang import Builder
from decimal import Decimal from decimal import Decimal
from kivy.uix.popup import Popup
from electrum.gui.kivy.i18n import _
Builder.load_string(''' Builder.load_string('''
<AddressLabel@Label> <AddressLabel@Label>
@ -95,11 +98,85 @@ Builder.load_string('''
default_size_hint: 1, None default_size_hint: 1, None
size_hint_y: None size_hint_y: None
height: self.minimum_height height: self.minimum_height
<AddressPopup@Popup>:
address: ''
balance: ''
status: ''
pk: ''
BoxLayout:
orientation: 'vertical'
ScrollView:
GridLayout:
cols: 1
height: self.minimum_height
size_hint_y: None
padding: '10dp'
spacing: '10dp'
GridLayout:
cols: 1
size_hint_y: None
height: self.minimum_height
spacing: '10dp'
BoxLabel:
text: _('Address')
value: root.address
BoxLabel:
text: _('Balance')
value: root.balance
BoxLabel:
text: _('Status')
value: root.status
TopLabel:
text: _('Private Key')
RefLabel:
id: pk_label
touched: True if not self.touched else True
data: root.pk
Widget:
size_hint: 1, 0.1
BoxLayout:
size_hint: 1, None
height: '48dp'
Button:
size_hint: 0.5, None
height: '48dp'
text: _('Hide key') if pk_label.data else _('Show key')
on_release:
setattr(pk_label, 'data', '') if pk_label.data else root.do_export(pk_label)
Button:
size_hint: 0.5, None
height: '48dp'
text: _('Use')
on_release: root.do_use()
Button:
size_hint: 0.5, None
height: '48dp'
text: _('Close')
on_release: root.dismiss()
''') ''')
from electrum.gui.kivy.i18n import _
from electrum.gui.kivy.uix.context_menu import ContextMenu class AddressPopup(Popup):
def __init__(self, parent, address, balance, status, **kwargs):
super(AddressPopup, self).__init__(**kwargs)
self.title = _('Address')
self.parent_dialog = parent
self.app = parent.app
self.address = address
self.status = status
self.balance = self.app.format_amount_and_units(balance)
def do_use(self):
self.dismiss()
self.parent_dialog.dismiss()
self.app.switch_to('receive')
self.app.receive_screen.set_address(self.address)
def do_export(self, pk_label):
self.app.export_private_keys(pk_label, self.address)
class AddressesDialog(Factory.Popup): class AddressesDialog(Factory.Popup):
@ -107,7 +184,6 @@ class AddressesDialog(Factory.Popup):
def __init__(self, app): def __init__(self, app):
Factory.Popup.__init__(self) Factory.Popup.__init__(self)
self.app = app self.app = app
self.context_menu = None
def get_card(self, addr, balance, is_used, label): def get_card(self, addr, balance, is_used, label):
ci = {} ci = {}
@ -119,7 +195,6 @@ class AddressesDialog(Factory.Popup):
return ci return ci
def update(self): def update(self):
self.menu_actions = [(_('Use'), self.do_use), (_('Details'), self.do_view)]
wallet = self.app.wallet wallet = self.app.wallet
if self.show_change == 0: if self.show_change == 0:
_list = wallet.get_receiving_addresses() _list = wallet.get_receiving_addresses()
@ -150,30 +225,12 @@ class AddressesDialog(Factory.Popup):
if not n: if not n:
self.app.show_error('No address matching your search') self.app.show_error('No address matching your search')
def do_use(self, obj): def show_item(self, obj):
self.hide_menu() address = obj.address
self.dismiss() c, u, x = self.app.wallet.get_addr_balance(address)
self.app.switch_to('receive')
self.app.receive_screen.set_address(obj.address)
def do_view(self, obj):
req = { 'address': obj.address, 'status' : obj.status }
status = obj.status
c, u, x = self.app.wallet.get_addr_balance(obj.address)
balance = c + u + x balance = c + u + x
if balance > 0: d = AddressPopup(self, address, balance, obj.status)
req['fund'] = balance d.open()
self.app.show_addr_details(req, status)
def ext_search(self, card, search): def ext_search(self, card, search):
return card['memo'].find(search) >= 0 or card['amount'].find(search) >= 0 return card['memo'].find(search) >= 0 or card['amount'].find(search) >= 0
def show_menu(self, obj):
self.hide_menu()
self.context_menu = ContextMenu(obj, self.menu_actions)
self.ids.box.add_widget(self.context_menu)
def hide_menu(self):
if self.context_menu is not None:
self.ids.box.remove_widget(self.context_menu)
self.context_menu = None

169
electrum/gui/kivy/uix/dialogs/invoices.py

@ -1,169 +0,0 @@
from kivy.app import App
from kivy.factory import Factory
from kivy.properties import ObjectProperty
from kivy.lang import Builder
from decimal import Decimal
Builder.load_string('''
<InvoicesLabel@Label>
#color: .305, .309, .309, 1
text_size: self.width, None
halign: 'left'
valign: 'top'
<InvoiceItem@CardItem>
requestor: ''
memo: ''
amount: ''
status: ''
date: ''
icon: 'atlas://electrum/gui/kivy/theming/light/important'
Image:
id: icon
source: root.icon
size_hint: None, 1
width: self.height *.54
mipmap: True
BoxLayout:
spacing: '8dp'
height: '32dp'
orientation: 'vertical'
Widget
InvoicesLabel:
text: root.requestor
shorten: True
Widget
InvoicesLabel:
text: root.memo
color: .699, .699, .699, 1
font_size: '13sp'
shorten: True
Widget
BoxLayout:
spacing: '8dp'
height: '32dp'
orientation: 'vertical'
Widget
InvoicesLabel:
text: root.amount
font_size: '15sp'
halign: 'right'
width: '110sp'
Widget
InvoicesLabel:
text: root.status
font_size: '13sp'
halign: 'right'
color: .699, .699, .699, 1
Widget
<InvoicesDialog@Popup>
id: popup
title: _('Invoices')
BoxLayout:
id: box
orientation: 'vertical'
spacing: '1dp'
ScrollView:
GridLayout:
cols: 1
id: invoices_container
size_hint: 1, None
height: self.minimum_height
spacing: '2dp'
padding: '12dp'
''')
from kivy.properties import BooleanProperty
from electrum.gui.kivy.i18n import _
from electrum.util import format_time
from electrum.paymentrequest import PR_UNPAID, PR_PAID, PR_UNKNOWN, PR_EXPIRED
from electrum.gui.kivy.uix.context_menu import ContextMenu
invoice_text = {
PR_UNPAID:_('Pending'),
PR_UNKNOWN:_('Unknown'),
PR_PAID:_('Paid'),
PR_EXPIRED:_('Expired')
}
pr_icon = {
PR_UNPAID: 'atlas://electrum/gui/kivy/theming/light/important',
PR_UNKNOWN: 'atlas://electrum/gui/kivy/theming/light/important',
PR_PAID: 'atlas://electrum/gui/kivy/theming/light/confirmed',
PR_EXPIRED: 'atlas://electrum/gui/kivy/theming/light/close'
}
class InvoicesDialog(Factory.Popup):
def __init__(self, app, screen, callback):
Factory.Popup.__init__(self)
self.app = app
self.screen = screen
self.callback = callback
self.cards = {}
self.context_menu = None
def get_card(self, pr):
key = pr.get_id()
ci = self.cards.get(key)
if ci is None:
ci = Factory.InvoiceItem()
ci.key = key
ci.screen = self
self.cards[key] = ci
ci.requestor = pr.get_requestor()
ci.memo = pr.get_memo()
amount = pr.get_amount()
if amount:
ci.amount = self.app.format_amount_and_units(amount)
status = self.app.wallet.invoices.get_status(ci.key)
ci.status = invoice_text[status]
ci.icon = pr_icon[status]
else:
ci.amount = _('No Amount')
ci.status = ''
exp = pr.get_expiration_date()
ci.date = format_time(exp) if exp else _('Never')
return ci
def update(self):
self.menu_actions = [('Pay', self.do_pay), ('Details', self.do_view), ('Delete', self.do_delete)]
invoices_list = self.ids.invoices_container
invoices_list.clear_widgets()
_list = self.app.wallet.invoices.sorted_list()
for pr in _list:
ci = self.get_card(pr)
invoices_list.add_widget(ci)
def do_pay(self, obj):
self.hide_menu()
self.dismiss()
pr = self.app.wallet.invoices.get(obj.key)
self.app.on_pr(pr)
def do_view(self, obj):
pr = self.app.wallet.invoices.get(obj.key)
pr.verify(self.app.wallet.contacts)
self.app.show_pr_details(pr.get_dict(), obj.status, True)
def do_delete(self, obj):
from .question import Question
def cb(result):
if result:
self.app.wallet.invoices.remove(obj.key)
self.hide_menu()
self.update()
d = Question(_('Delete invoice?'), cb)
d.open()
def show_menu(self, obj):
self.hide_menu()
self.context_menu = ContextMenu(obj, self.menu_actions)
self.ids.box.add_widget(self.context_menu)
def hide_menu(self):
if self.context_menu is not None:
self.ids.box.remove_widget(self.context_menu)
self.context_menu = None

155
electrum/gui/kivy/uix/dialogs/lightning_channels.py

@ -4,10 +4,10 @@ from kivy.lang import Builder
from kivy.factory import Factory from kivy.factory import Factory
from kivy.uix.popup import Popup from kivy.uix.popup import Popup
from kivy.clock import Clock from kivy.clock import Clock
from electrum.gui.kivy.uix.context_menu import ContextMenu
from electrum.util import bh2u from electrum.util import bh2u
from electrum.lnutil import LOCAL, REMOTE, format_short_channel_id from electrum.lnutil import LOCAL, REMOTE, format_short_channel_id
from electrum.gui.kivy.i18n import _ from electrum.gui.kivy.i18n import _
from .question import Question
Builder.load_string(r''' Builder.load_string(r'''
<LightningChannelItem@CardItem> <LightningChannelItem@CardItem>
@ -71,38 +71,11 @@ Builder.load_string(r'''
text: _('New channel...') text: _('New channel...')
on_press: popup.app.popup_dialog('lightning_open_channel_dialog') on_press: popup.app.popup_dialog('lightning_open_channel_dialog')
<ChannelDetailsItem@BoxLayout>:
canvas.before:
Color:
rgba: 0.5, 0.5, 0.5, 1
Rectangle:
size: self.size
pos: self.pos
value: ''
Label:
text: root.value
text_size: self.size # this makes the text not overflow, but wrap
<ChannelDetailsRow@BoxLayout>:
keyName: ''
value: ''
ChannelDetailsItem:
value: root.keyName
size_hint_x: 0.5 # this makes the column narrower
# see https://blog.kivy.org/2014/07/wrapping-text-in-kivys-label/
ScrollView:
Label:
text: root.value
size_hint_y: None
text_size: self.width, None
height: self.texture_size[1]
<ChannelDetailsList@RecycleView>: <ChannelDetailsList@RecycleView>:
scroll_type: ['bars', 'content'] scroll_type: ['bars', 'content']
scroll_wheel_distance: dp(114) scroll_wheel_distance: dp(114)
bar_width: dp(10) bar_width: dp(10)
viewclass: 'ChannelDetailsRow' viewclass: 'BoxLabel'
RecycleBoxLayout: RecycleBoxLayout:
default_size: None, dp(56) default_size: None, dp(56)
default_size_hint: 1, None default_size_hint: 1, None
@ -114,64 +87,102 @@ Builder.load_string(r'''
<ChannelDetailsPopup@Popup>: <ChannelDetailsPopup@Popup>:
id: popuproot id: popuproot
data: [] data: []
ChannelDetailsList: BoxLayout:
data: popuproot.data orientation: 'vertical'
ScrollView:
ChannelDetailsList:
data: popuproot.data
Widget:
size_hint: 1, 0.1
BoxLayout:
size_hint: 1, None
height: '48dp'
Button:
size_hint: 0.5, None
height: '48dp'
text: _('Close channel')
on_release: root.close()
Button:
size_hint: 0.5, None
height: '48dp'
text: _('Force-close')
on_release: root.force_close()
Button:
size_hint: 0.5, None
height: '48dp'
text: _('Dismiss')
on_release: root.dismiss()
''') ''')
class ChannelDetailsPopup(Popup): class ChannelDetailsPopup(Popup):
def __init__(self, data, **kwargs):
super(ChannelDetailsPopup,self).__init__(**kwargs)
self.data = data
class LightningChannelsDialog(Factory.Popup): def __init__(self, chan, app, **kwargs):
def __init__(self, app): super(ChannelDetailsPopup,self).__init__(**kwargs)
super(LightningChannelsDialog, self).__init__()
self.clocks = []
self.app = app self.app = app
self.context_menu = None self.chan = chan
self.app.wallet.network.register_callback(self.on_channels, ['channels']) self.title = _('Channel details')
self.app.wallet.network.register_callback(self.on_channel, ['channel']) self.data = [{'text': key, 'value': str(value)} for key, value in self.details().items()]
self.update()
def details(self):
def show_channel_details(self, obj): chan = self.chan
p = Factory.ChannelDetailsPopup() return {
p.title = _('Details for channel ') + format_short_channel_id(obj.chan.short_channel_id) _('Short Chan ID'): format_short_channel_id(chan.short_channel_id),
p.data = [{'keyName': key, 'value': str(obj.details[key])} for key in obj.details.keys()] _('Initiator'): 'Local' if chan.constraints.is_initiator else 'Remote',
p.open() _('State'): chan.get_state(),
_('Capacity'): self.app.format_amount_and_units(chan.constraints.capacity),
def close_channel(self, obj): _('Can send'): self.app.format_amount_and_units(chan.available_to_spend(LOCAL) // 1000),
_('Current feerate'): str(chan.get_latest_feerate(LOCAL)),
_('Node ID'): bh2u(chan.node_id),
_('Channel ID'): bh2u(chan.channel_id),
_('Funding TXID'): chan.funding_outpoint.txid,
}
def close(self):
Question(_('Close channel?'), self._close).open()
def _close(self, b):
if not b:
return
loop = self.app.wallet.network.asyncio_loop loop = self.app.wallet.network.asyncio_loop
coro = asyncio.run_coroutine_threadsafe(self.app.wallet.lnworker.close_channel(obj._chan.channel_id), loop) coro = asyncio.run_coroutine_threadsafe(self.app.wallet.lnworker.close_channel(self._chan.channel_id), loop)
try: try:
coro.result(5) coro.result(5)
self.app.show_info(_('Channel closed')) self.app.show_info(_('Channel closed'))
except Exception as e: except Exception as e:
self.app.show_info(_('Could not close channel: ') + repr(e)) # repr because str(Exception()) == '' self.app.show_info(_('Could not close channel: ') + repr(e)) # repr because str(Exception()) == ''
def force_close_channel(self, obj): def force_close(self):
if obj._chan.get_state() == 'CLOSED': Question(_('Force-close channel?'), self._force_close).open()
def _force_close(self, b):
if not b:
return
if self.chan.get_state() == 'CLOSED':
self.app.show_error(_('Channel already closed')) self.app.show_error(_('Channel already closed'))
return return
loop = self.app.wallet.network.asyncio_loop loop = self.app.wallet.network.asyncio_loop
coro = asyncio.run_coroutine_threadsafe(self.app.wallet.lnworker.force_close_channel(obj._chan.channel_id), loop) coro = asyncio.run_coroutine_threadsafe(self.app.wallet.lnworker.force_close_channel(self.chan.channel_id), loop)
try: try:
coro.result(1) coro.result(1)
self.app.show_info(_('Channel closed, you may need to wait at least {} blocks, because of CSV delays'.format(obj._chan.config[REMOTE].to_self_delay))) self.app.show_info(_('Channel closed, you may need to wait at least {} blocks, because of CSV delays'.format(self.chan.config[REMOTE].to_self_delay)))
except Exception as e: except Exception as e:
self.app.show_info(_('Could not force close channel: ') + repr(e)) # repr because str(Exception()) == '' self.app.show_info(_('Could not force close channel: ') + repr(e)) # repr because str(Exception()) == ''
def show_menu(self, obj):
self.hide_menu()
self.context_menu = ContextMenu(obj, [
(_("Force close"), self.force_close_channel),
(_("Co-op close"), self.close_channel),
(_("Details"), self.show_channel_details)])
self.ids.box.add_widget(self.context_menu)
def hide_menu(self): class LightningChannelsDialog(Factory.Popup):
if self.context_menu is not None:
self.ids.box.remove_widget(self.context_menu) def __init__(self, app):
self.context_menu = None super(LightningChannelsDialog, self).__init__()
self.clocks = []
self.app = app
self.app.wallet.network.register_callback(self.on_channels, ['channels'])
self.app.wallet.network.register_callback(self.on_channel, ['channel'])
self.update()
def show_item(self, obj):
p = ChannelDetailsPopup(obj._chan, self.app)
p.open()
def format_fields(self, chan): def format_fields(self, chan):
labels = {} labels = {}
@ -213,18 +224,6 @@ class LightningChannelsDialog(Factory.Popup):
item = Factory.LightningChannelItem() item = Factory.LightningChannelItem()
item.screen = self item.screen = self
item.active = i.node_id in lnworker.peers item.active = i.node_id in lnworker.peers
item.details = self.channel_details(i)
item._chan = i item._chan = i
self.update_item(item) self.update_item(item)
channel_cards.add_widget(item) channel_cards.add_widget(item)
def channel_details(self, chan):
return {_('Node ID'): bh2u(chan.node_id),
_('Channel ID'): bh2u(chan.channel_id),
_('Capacity'): self.app.format_amount_and_units(chan.constraints.capacity),
_('Funding TXID'): chan.funding_outpoint.txid,
_('Short Chan ID'): bh2u(chan.short_channel_id) if chan.short_channel_id else _('Not available'),
_('Available to spend'): self.app.format_amount_and_units(chan.available_to_spend(LOCAL) // 1000),
_('State'): chan.get_state(),
_('Initiator'): 'Opened/funded by us' if chan.constraints.is_initiator else 'Opened/funded by remote party',
_('Current feerate'): chan.get_latest_feerate(LOCAL)}

65
electrum/gui/kivy/uix/dialogs/lightning_invoices.py

@ -1,65 +0,0 @@
from kivy.factory import Factory
from kivy.lang import Builder
from electrum.gui.kivy.i18n import _
from kivy.uix.recycleview import RecycleView
from electrum.gui.kivy.uix.context_menu import ContextMenu
Builder.load_string('''
<Item@CardItem>
addr: ''
desc: ''
screen: None
BoxLayout:
orientation: 'vertical'
Label
text: root.addr
text_size: self.width, None
shorten: True
Label
text: root.desc if root.desc else _('No description')
text_size: self.width, None
shorten: True
font_size: '10dp'
<LightningInvoicesDialog@Popup>
id: popup
title: _('Lightning Invoices')
BoxLayout:
orientation: 'vertical'
id: box
RecycleView:
viewclass: 'Item'
id: recycleview
data: []
RecycleBoxLayout:
default_size: None, dp(56)
default_size_hint: 1, None
size_hint_y: None
height: self.minimum_height
orientation: 'vertical'
''')
class LightningInvoicesDialog(Factory.Popup):
def __init__(self, report, callback):
super().__init__()
self.context_menu = None
self.callback = callback
self.menu_actions = [(_('Show'), self.do_show)]
for addr, preimage, pay_req in report['unsettled']:
self.ids.recycleview.data.append({'screen': self, 'addr': pay_req, 'desc': dict(addr.tags).get('d', '')})
def do_show(self, obj):
self.hide_menu()
self.dismiss()
self.callback(obj.addr)
def show_menu(self, obj):
self.hide_menu()
self.context_menu = ContextMenu(obj, self.menu_actions)
self.ids.box.add_widget(self.context_menu)
def hide_menu(self):
if self.context_menu is not None:
self.ids.box.remove_widget(self.context_menu)
self.context_menu = None

33
electrum/gui/kivy/uix/dialogs/request_dialog.py

@ -42,30 +42,33 @@ Builder.load_string('''
Button: Button:
size_hint: 1, None size_hint: 1, None
height: '48dp' height: '48dp'
text: _('Copy') text: _('Delete')
on_release: on_release: root.delete_dialog()
root.copy_to_clipboard() IconButton:
icon: 'atlas://electrum/gui/kivy/theming/light/copy'
size_hint: 0.5, None
height: '48dp'
on_release: root.copy_to_clipboard()
IconButton: IconButton:
icon: 'atlas://electrum/gui/kivy/theming/light/share' icon: 'atlas://electrum/gui/kivy/theming/light/share'
size_hint: 0.6, None size_hint: 0.5, None
height: '48dp' height: '48dp'
on_release: s.parent.do_share() on_release: root.do_share()
Button: Button:
size_hint: 1, None size_hint: 1, None
height: '48dp' height: '48dp'
text: _('Close') text: _('Close')
on_release: on_release: popup.dismiss()
popup.dismiss()
''') ''')
class RequestDialog(Factory.Popup): class RequestDialog(Factory.Popup):
def __init__(self, title, data, key): def __init__(self, title, data, key):
Factory.Popup.__init__(self) Factory.Popup.__init__(self)
self.app = App.get_running_app() self.app = App.get_running_app()
self.title = title self.title = title
self.data = data self.data = data
self.key = key self.key = key
#self.text_for_clipboard = text_for_clipboard if text_for_clipboard else data
def on_open(self): def on_open(self):
self.ids.qr.set_data(self.data) self.ids.qr.set_data(self.data)
@ -80,3 +83,17 @@ class RequestDialog(Factory.Popup):
Clipboard.copy(self.data) Clipboard.copy(self.data)
msg = _('Text copied to clipboard.') msg = _('Text copied to clipboard.')
Clock.schedule_once(lambda dt: self.app.show_info(msg)) Clock.schedule_once(lambda dt: self.app.show_info(msg))
def do_share(self):
self.app.do_share(self.data, _("Share Bitcoin Request"))
self.dismiss()
def delete_dialog(self):
from .question import Question
def cb(result):
if result:
self.app.wallet.delete_request(self.key)
self.dismiss()
self.app.receive_screen.update()
d = Question(_('Delete request?'), cb)
d.open()

16
electrum/gui/kivy/uix/dialogs/tx_dialog.py

@ -98,6 +98,11 @@ Builder.load_string('''
height: '48dp' height: '48dp'
icon: 'atlas://electrum/gui/kivy/theming/light/qrcode' icon: 'atlas://electrum/gui/kivy/theming/light/qrcode'
on_release: root.show_qr() on_release: root.show_qr()
Button:
size_hint: 0.5, None
height: '48dp'
text: _('Label')
on_release: root.label_dialog()
Button: Button:
size_hint: 0.5, None size_hint: 0.5, None
height: '48dp' height: '48dp'
@ -271,3 +276,14 @@ class TxDialog(Factory.Popup):
self.dismiss() self.dismiss()
d = Question(question, on_prompt) d = Question(question, on_prompt)
d.open() d.open()
def label_dialog(self):
from .label_dialog import LabelDialog
key = self.tx.txid()
text = self.app.wallet.get_label(key)
def callback(text):
self.app.wallet.set_label(key, text)
self.update()
self.app.history_screen.update()
d = LabelDialog(_('Enter Transaction Label'), text, callback)
d.open()

53
electrum/gui/kivy/uix/screens.py

@ -33,7 +33,6 @@ from electrum import simple_config
from electrum.lnaddr import lndecode from electrum.lnaddr import lndecode
from electrum.lnutil import RECEIVED, SENT, PaymentFailure from electrum.lnutil import RECEIVED, SENT, PaymentFailure
from .context_menu import ContextMenu
from .dialogs.question import Question from .dialogs.question import Question
from .dialogs.lightning_open_channel import LightningOpenChannelDialog from .dialogs.lightning_open_channel import LightningOpenChannelDialog
@ -55,8 +54,6 @@ class CScreen(Factory.Screen):
action_view = ObjectProperty(None) action_view = ObjectProperty(None)
loaded = False loaded = False
kvname = None kvname = None
context_menu = None
menu_actions = []
app = App.get_running_app() app = App.get_running_app()
def _change_action_view(self): def _change_action_view(self):
@ -94,17 +91,7 @@ class CScreen(Factory.Screen):
self.dispatch('on_deactivate') self.dispatch('on_deactivate')
def on_deactivate(self): def on_deactivate(self):
self.hide_menu() pass
def hide_menu(self):
if self.context_menu is not None:
self.remove_widget(self.context_menu)
self.context_menu = None
def show_menu(self, obj):
self.hide_menu()
self.context_menu = ContextMenu(obj, self.menu_actions)
self.add_widget(self.context_menu)
# note: this list needs to be kept in sync with another in qt # note: this list needs to be kept in sync with another in qt
@ -130,24 +117,15 @@ class HistoryScreen(CScreen):
def __init__(self, **kwargs): def __init__(self, **kwargs):
self.ra_dialog = None self.ra_dialog = None
super(HistoryScreen, self).__init__(**kwargs) super(HistoryScreen, self).__init__(**kwargs)
self.menu_actions = [ ('Label', self.label_dialog), ('Details', self.show_tx)]
def show_tx(self, obj): def show_item(self, obj):
print(obj)
key = obj.key key = obj.key
tx = self.app.wallet.db.get_transaction(key) tx = self.app.wallet.db.get_transaction(key)
if not tx: if not tx:
return return
self.app.tx_dialog(tx) self.app.tx_dialog(tx)
def label_dialog(self, obj):
from .dialogs.label_dialog import LabelDialog
key = obj.key
text = self.app.wallet.get_label(key)
def callback(text):
self.app.wallet.set_label(key, text)
self.update()
d = LabelDialog(_('Enter Transaction Label'), text, callback)
d.open()
def get_card(self, tx_item): #tx_hash, tx_mined_status, value, balance): def get_card(self, tx_item): #tx_hash, tx_mined_status, value, balance):
is_lightning = tx_item.get('lightning', False) is_lightning = tx_item.get('lightning', False)
@ -406,7 +384,6 @@ class ReceiveScreen(CScreen):
def __init__(self, **kwargs): def __init__(self, **kwargs):
super(ReceiveScreen, self).__init__(**kwargs) super(ReceiveScreen, self).__init__(**kwargs)
self.menu_actions = [(_('Show'), self.do_show), (_('Delete'), self.delete_request_dialog)]
Clock.schedule_interval(lambda dt: self.update(), 5) Clock.schedule_interval(lambda dt: self.update(), 5)
def expiry(self): def expiry(self):
@ -440,10 +417,6 @@ class ReceiveScreen(CScreen):
amount = Decimal(a) * pow(10, self.app.decimal_point()) amount = Decimal(a) * pow(10, self.app.decimal_point())
return create_bip21_uri(self.screen.address, amount, self.screen.message) return create_bip21_uri(self.screen.address, amount, self.screen.message)
def do_share(self):
uri = self.get_URI()
self.app.do_share(uri, _("Share Bitcoin Request"))
def do_copy(self): def do_copy(self):
uri = self.get_URI() uri = self.get_URI()
self.app._clipboard.copy(uri) self.app._clipboard.copy(uri)
@ -498,8 +471,7 @@ class ReceiveScreen(CScreen):
requests_container = self.screen.ids.requests_container requests_container = self.screen.ids.requests_container
requests_container.data = [self.get_card(item) for item in _list if item.get('status') != PR_PAID] requests_container.data = [self.get_card(item) for item in _list if item.get('status') != PR_PAID]
def do_show(self, obj): def show_item(self, obj):
self.hide_menu()
self.app.show_request(obj.is_lightning, obj.key) self.app.show_request(obj.is_lightning, obj.key)
def expiration_dialog(self, obj): def expiration_dialog(self, obj):
@ -523,24 +495,7 @@ class ReceiveScreen(CScreen):
d = Question(_('Delete expired requests?'), callback) d = Question(_('Delete expired requests?'), callback)
d.open() d.open()
def delete_request_dialog(self, req):
def cb(result):
if result:
self.app.wallet.delete_request(req.key)
self.hide_menu()
self.update()
d = Question(_('Delete request?'), cb)
d.open()
def show_menu(self, obj):
self.hide_menu()
self.context_menu = ContextMenu(obj, self.menu_actions)
self.add_widget(self.context_menu)
def hide_menu(self):
if self.context_menu is not None:
self.remove_widget(self.context_menu)
self.context_menu = None
class TabbedCarousel(Factory.TabbedPanel): class TabbedCarousel(Factory.TabbedPanel):
'''Custom TabbedPanel using a carousel used in the Main Screen '''Custom TabbedPanel using a carousel used in the Main Screen

89
electrum/gui/kivy/uix/ui_screens/invoice.kv

@ -1,89 +0,0 @@
#:import Decimal decimal.Decimal
Popup:
id: popup
is_invoice: True
amount: 0
requestor: ''
exp: ''
description: ''
status: ''
signature: ''
isaddr: ''
fund: 0
pk: ''
title: _('Invoice') if popup.is_invoice else _('Request')
tx_hash: ''
BoxLayout:
orientation: 'vertical'
ScrollView:
GridLayout:
cols: 1
height: self.minimum_height
size_hint_y: None
padding: '10dp'
spacing: '10dp'
GridLayout:
cols: 1
size_hint_y: None
height: self.minimum_height
spacing: '10dp'
BoxLabel:
text: (_('Status') if popup.amount or popup.is_invoice or popup.isaddr == 'y' else _('Amount received')) if root.status else ''
value: root.status
BoxLabel:
text: _('Request amount') if root.amount else ''
value: app.format_amount_and_units(root.amount) if root.amount else ''
BoxLabel:
text: _('Requestor') if popup.is_invoice else _('Address')
value: root.requestor
BoxLabel:
text: _('Signature') if root.signature else ''
value: root.signature
BoxLabel:
text: _('Expiration') if root.exp else ''
value: root.exp
BoxLabel:
text: _('Description') if root.description else ''
value: root.description
BoxLabel:
text: _('Balance') if popup.fund else ''
value: app.format_amount_and_units(root.fund) if root.fund else ''
TopLabel:
text: _('Private Key')
RefLabel:
id: pk_label
touched: True if not self.touched else True
data: root.pk
TopLabel:
text: _('Outputs') if popup.is_invoice else ''
OutputList:
id: output_list
TopLabel:
text: _('Transaction ID') if popup.tx_hash else ''
TxHashLabel:
data: popup.tx_hash
name: _('Transaction ID')
Widget:
size_hint: 1, 0.1
BoxLayout:
size_hint: 1, None
height: '48dp'
Widget:
size_hint: 0.5, None
height: '48dp'
Button:
size_hint: 2, None
height: '48dp'
text: _('Close')
on_release: popup.dismiss()
Button:
size_hint: 2, None
height: '48dp'
text: _('Hide private key') if pk_label.data else _('Export private key')
on_release:
setattr(pk_label, 'data', '') if pk_label.data else popup.export(pk_label, popup.requestor)
Loading…
Cancel
Save