Skip to content

Commit

Permalink
Add context menu to reorder starred taxa; closes #17
Browse files Browse the repository at this point in the history
  • Loading branch information
JWCook committed Jun 4, 2020
1 parent 5354723 commit 9160374
Show file tree
Hide file tree
Showing 13 changed files with 166 additions and 87 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,9 @@ The additional tabs on the taxon screen contain:
* Most frequently viewed taxa
* Starred taxa

To save a particular taxon for future reference, click the ✩ icon next to it.
To save a particular taxon for future reference, click the ✩ icon in the top left of its info panel,
and it will be saved in the ★ tab. These items can be re-ordered via **Right-click** -> **Move to top**.
(Unfortunately, drag-and-drop functionality is not currently possible for list items).

### Metadata
**Right-click** an image and select **Copy Flickr tags** to copy keyword tags compatible with Flickr.
Expand Down
13 changes: 11 additions & 2 deletions kv/local_photo_context_menu.kv → kv/context_menus.kv
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
#: import PhotoContextMenu naturtag.widgets.PhotoContextMenu
#: import ObjectContextMenu naturtag.widgets.ObjectContextMenu
#: import PhotoContextMenuItem naturtag.widgets.PhotoContextMenuItem
#: import ListContextMenuItem naturtag.widgets.ListContextMenuItem

<PhotoContextMenu>:

<PhotoContextMenu@ObjectContextMenu>:
visible: False
PhotoContextMenuItem:
id: view_taxon_ctx
Expand Down Expand Up @@ -30,3 +32,10 @@
# text: "Search for observations of this taxon"
# ContextMenuTextItem:
# text: "Search for similar species"


<StarredTaxonContextMenu@ObjectContextMenu>:
visible: False
ListContextMenuItem:
id: move_to_top_ctx
text: "Move to top"
1 change: 0 additions & 1 deletion kv/image_selection.kv
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
# Right-click context menu for images
PhotoContextMenu:
id: context_menu
visible: False
cancel_handler_widget: layout
BoxLayout:
orientation: 'vertical'
Expand Down
103 changes: 54 additions & 49 deletions kv/taxon.kv
Original file line number Diff line number Diff line change
Expand Up @@ -3,58 +3,63 @@

<TaxonScreen>:
name: 'taxon'

BoxLayout:
orientation: 'vertical'
FloatLayout:
id: layout
# Right-click context menu for images
StarredTaxonContextMenu:
id: context_menu
cancel_handler_widget: layout
BoxLayout:
# Taxon selection: search, history, etc.
MDTabs:
id: taxon_selection_tabs
size_hint_x: .4
size_hint_min_x: dp(500)
size_hint_max_x: dp(700)
background_color: app.theme_cls.primary_color
TaxonSearchTab:
id: search_tab
TaxonSearchResultsTab:
id: search_results_tab
TaxonHistoryTab:
id: history_tab
FrequentTaxaTab:
id: frequent_tab
StarredTaxaTab:
id: starred_tab
orientation: 'vertical'
BoxLayout:
# Taxon selection: search, history, etc.
MDTabs:
id: taxon_selection_tabs
size_hint_x: .4
size_hint_min_x: dp(500)
size_hint_max_x: dp(700)
background_color: app.theme_cls.primary_color
TaxonSearchTab:
id: search_tab
TaxonSearchResultsTab:
id: search_results_tab
TaxonHistoryTab:
id: history_tab
FrequentTaxaTab:
id: frequent_tab
StarredTaxaTab:
id: starred_tab

# Info display for selected taxon
MDBoxLayout:
orientation: 'vertical'
padding: dp(20)
md_bg_color: app.theme_cls.bg_dark
MDGridLayout:
cols: 2
spacing: dp(10)
size_hint_y: .7
size_hint_min_y: dp(300)
size_hint_max_y: dp(650)
MDBoxLayout:
orientation: 'vertical'
# Info display for selected taxon
MDBoxLayout:
orientation: 'vertical'
padding: dp(20)
md_bg_color: app.theme_cls.bg_dark
MDGridLayout:
cols: 2
spacing: dp(10)
size_hint_max_x: dp(500)
CachedAsyncImage:
id: selected_taxon_photo
size_hint_max_y: dp(500)
MDBoxLayout:
size_hint_max_x: dp(500)
orientation: 'vertical'
ScrollViewY:
MDList:
id: basic_info_section
LinksSection:
id: taxon_links
TaxonomySection:
id: taxonomy_section
StatusBar:
id: status_bar
size_hint_y: .7
size_hint_min_y: dp(300)
size_hint_max_y: dp(650)
MDBoxLayout:
orientation: 'vertical'
spacing: dp(10)
size_hint_max_x: dp(500)
CachedAsyncImage:
id: selected_taxon_photo
size_hint_max_y: dp(500)
MDBoxLayout:
size_hint_max_x: dp(500)
orientation: 'vertical'
ScrollViewY:
MDList:
id: basic_info_section
LinksSection:
id: taxon_links
TaxonomySection:
id: taxonomy_section
StatusBar:
id: status_bar

# Taxon links
<LinksSection@MDBoxLayout>:
Expand Down
3 changes: 3 additions & 0 deletions kv/widgets.kv
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
theme_text_color: 'Primary'
size_hint_max_y: dp(60)

<TooltipIconButton>:
padding: 0, 0, 0, 0

# Images
# ------------------------------

Expand Down
4 changes: 3 additions & 1 deletion naturtag/app/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,9 @@ def init_controllers(self, screens):
def get_taxon_list_item(self, *args, **kwargs):
""" Get a new :py:class:`.TaxonListItem with event binding """
item = TaxonListItem(*args, **kwargs)
item.bind(on_release=lambda x: self.taxon_view_controller.select_taxon(x.taxon))
# If TaxonListItem's disable_button is set, don't set button action
if not kwargs.get('disable_button'):
item.bind(on_release=lambda x: self.taxon_view_controller.select_taxon(x.taxon))
return item


Expand Down
2 changes: 1 addition & 1 deletion naturtag/app/screens.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
'widgets',
'main',
'autocomplete',
'local_photo_context_menu',
'context_menus',
'taxon_search',
'taxon_selection',
]
Expand Down
2 changes: 1 addition & 1 deletion naturtag/controllers/image_selection_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def on_image_click(self, instance, touch):
# Right-click: Open context menu for the image
elif touch.button == 'right':
self.context_menu.show(*get_app().root_window.mouse_pos)
self.context_menu.selected_image = instance
self.context_menu.ref = instance
# Enable 'view taxon/observation' menu items, if applicable
self.context_menu.ids.view_taxon_ctx.disabled = not instance.metadata.taxon_id
self.context_menu.ids.view_observation_ctx.disabled = not instance.metadata.observation_id
Expand Down
40 changes: 37 additions & 3 deletions naturtag/controllers/taxon_selection_controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ def __init__(self, screen):
self.frequent_tab = screen.frequent_tab
self.starred_tab = screen.starred_tab

# Context menu
self.context_menu = screen.context_menu
self.context_menu.ids.move_to_top_ctx.bind(on_release=self.move_starred_to_top)

# Various taxon lists
self.taxon_history_ids = []
self.taxon_history_map = {}
Expand Down Expand Up @@ -82,15 +86,21 @@ def add_frequent_taxon(self, taxon_id: int):
def add_star(self, taxon_id: int):
""" Add a taxon to Starred list """
logger.info(f'Adding taxon to starred: {taxon_id}')
item = get_app().get_taxon_list_item(taxon_id=taxon_id, parent_tab=self.starred_tab)
if taxon_id not in self.starred_taxa_ids:
self.starred_taxa_ids.append(taxon_id)
self.starred_taxa_map[taxon_id] = item
self.starred_taxa_list.add_widget(item, len(self.starred_taxa_list.children))

item = get_app().get_taxon_list_item(
taxon_id=taxon_id,
parent_tab=self.starred_tab,
disable_button=True,
)
item.bind(on_touch_down=self.on_starred_taxon_click)
# Add X (remove) button
remove_button = StarButton(taxon_id, icon='close')
remove_button.bind(on_release=lambda x: self.remove_star(x.taxon_id))
item.add_widget(remove_button)
self.starred_taxa_map[taxon_id] = item
self.starred_taxa_list.add_widget(item, len(self.starred_taxa_list.children))

def remove_star(self, taxon_id: int):
""" Remove a taxon from Starred list """
Expand All @@ -103,6 +113,30 @@ def is_starred(self, taxon_id: int) -> bool:
""" Check if the specified taxon is in the Starred list """
return taxon_id in self.starred_taxa_map

def on_starred_taxon_click(self, instance, touch):
""" Event handler for clicking a item from starred taxa list """
if not instance.collide_point(*touch.pos):
return
# Right-click: Open context menu
elif touch.button == 'right':
self.context_menu.show(*get_app().root_window.mouse_pos)
self.context_menu.ref = instance
# self.context_menu.ids.view_taxon_ctx.disabled = not instance.metadata.taxon_id
# Middle-click: remove item
elif touch.button == 'middle':
self.remove_star(instance.taxon.id)
# Left-cliok: select taxon
else:
get_app().select_taxon(instance.taxon)

def move_starred_to_top(self, instance):
""" Move a starred taxon to the top of the list, both in the UI and in persisted list """
lst = self.starred_taxa_ids
lst.append(lst.pop(lst.index(instance.taxon_id)))
item = self.starred_taxa_map[instance.taxon_id]
self.starred_taxa_list.remove_widget(item)
self.starred_taxa_list.add_widget(item, len(self.starred_taxa_list.children))

def get_frequent_taxon_idx(self, list_item) -> int:
""" Get sort index for frequently viewed taxa (by number of views, descending) """
num_views = self.frequent_taxa_ids.get(list_item.taxon.id, 0)
Expand Down
4 changes: 2 additions & 2 deletions naturtag/widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ def truncate(text: str) -> str:
from naturtag.widgets.images import CachedAsyncImage, IconicTaxaIcon, ImageMetaTile
from naturtag.widgets.inputs import DropdownTextField
from naturtag.widgets.labels import HideableTooltip, TooltipLabel
from naturtag.widgets.lists import SortableList, SwitchListItem, TextInputListItem, TaxonListItem, ThumbnailListItem
from naturtag.widgets.menus import PhotoContextMenu, PhotoContextMenuItem
from naturtag.widgets.tabs import Tab
from naturtag.widgets.lists import SortableList, SwitchListItem, TextInputListItem, TaxonListItem, ThumbnailListItem
from naturtag.widgets.menus import ObjectContextMenu, AutoHideMenuItem, PhotoContextMenuItem, ListContextMenuItem
from naturtag.widgets.taxon_autocomplete import TaxonAutocompleteSearch

2 changes: 1 addition & 1 deletion naturtag/widgets/labels.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
# TODO: Debug root cause of rogue tooltips!
class HideableTooltip(MDTooltip):
"""
This is a workaround for unexpected bahvior with tooltips and tabs. If a HideableTooltip is
This is a workaround for unexpected behavior with tooltips and tabs. If a HideableTooltip is
in an unselected tab, it will always report that the mouse cursor does not intersect it.
"""
def __init__(self, is_visible_callback, **kwargs):
Expand Down
38 changes: 24 additions & 14 deletions naturtag/widgets/lists.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
from kivy.uix.behaviors import ButtonBehavior
from kivy.core.clipboard import Clipboard
from kivymd.uix.list import MDList, ILeftBody, ILeftBodyTouch, OneLineListItem
from kivymd.uix.list import ThreeLineAvatarIconListItem
from kivymd.uix.selectioncontrol import MDSwitch
from kivymd.uix.textfield import MDTextFieldRound

from naturtag.app import alert
from naturtag.models import Taxon
from naturtag.app import get_app
from naturtag.widgets import truncate, CachedAsyncImage, HideableTooltip
from naturtag.widgets import truncate, CachedAsyncImage, HideableTooltip, Tab


class SortableList(MDList):
Expand All @@ -30,27 +30,37 @@ class SwitchListItem(ILeftBodyTouch, MDSwitch):
class TextInputListItem(OneLineListItem, MDTextFieldRound):
""" Switch that works as a list item """

from kivy.core.clipboard import Clipboard
from naturtag.app import alert

class TaxonListItem(ThreeLineAvatarIconListItem, HideableTooltip):
# class TaxonListItem(ThreeLineAvatarIconListItem, HideableTooltip):
class TaxonListItem(ThreeLineAvatarIconListItem):
""" Class that displays condensed taxon info as a list item """
def __init__(self, taxon=None, taxon_id=None, parent_tab=None,**kwargs):
def __init__(
self,
taxon: Taxon=None,
taxon_id: int=None,
parent_tab: Tab=None,
disable_button: bool=False,
**kwargs,
):
if not taxon and not taxon_id:
raise ValueError('Must provide either a taxon object or ID')
if not disable_button:
self.bind(on_touch_down=self._on_touch_down)
taxon = taxon or Taxon.from_id(taxon_id)
self.disable_button = disable_button

super().__init__(
font_style='H6',
text=taxon.name,
secondary_text=taxon.rank,
tertiary_text=taxon.preferred_common_name,
tooltip_text=(
f'ID: {taxon.id}\n'
f'Ancestry: {truncate(taxon.ancestry_str)}\n'
f'Children: {len(taxon.child_taxa)}'
),
is_visible_callback=self.is_visible,
# TODO: Need to fine-tune tooltip behavior before enabling again
# tooltip_text=(
# f'ID: {taxon.id}\n'
# f'Ancestry: {truncate(taxon.ancestry_str)}\n'
# f'Children: {len(taxon.child_taxa)}'
# ),
# is_visible_callback=self.is_visible,
**kwargs,
)

Expand All @@ -62,7 +72,7 @@ def __init__(self, taxon=None, taxon_id=None, parent_tab=None,**kwargs):
self.taxon = taxon
self.add_widget(ThumbnailListItem(source=taxon.thumbnail_url or taxon.icon_path))

def on_touch_down(self, touch):
def _on_touch_down(self, instance, touch):
""" Copy text on right-click """
if not self.collide_point(*touch.pos):
return
Expand Down

0 comments on commit 9160374

Please sign in to comment.