Add addition/subtract/divide blending modes

Changes:
* Added SeparatorInView() to add separators inside a ListBox or ComboBox.
* Added BlendModeItem() to represent each item in the blending modes
  ComboBox.
* Now a ComboBox can contain any kind of widgets as children (not just
  ListItem). This is required to add separators in the blending modes
  ComboBox.

Feature request:
https://community.aseprite.org/t/additive-blending-feature/121
This commit is contained in:
David Capello 2017-07-24 15:13:32 -03:00
parent 4f4b21a521
commit 14ba0ab411
15 changed files with 229 additions and 91 deletions

View File

@ -171,6 +171,9 @@ Ignore this chunk if you find the new palette chunk (0x2019)
Saturation = 13
Color = 14
Luminosity = 15
Addition = 16
Subtract = 17
Divide = 18
BYTE Opacity
Note: valid only if file header flags field has bit 1 set
BYTE[3] For future (set to zero)

View File

@ -21,6 +21,7 @@
#include "app/ui/keyboard_shortcuts.h"
#include "app/ui/search_entry.h"
#include "app/ui/select_accelerator.h"
#include "app/ui/separator_in_view.h"
#include "app/ui/skin/skin_theme.h"
#include "base/bind.h"
#include "base/fs.h"
@ -502,10 +503,8 @@ private:
continue;
if (!group) {
group = new Separator(
group = new SeparatorInView(
section()->children()[sectionIdx]->text(), HORIZONTAL);
group->setStyle(SkinTheme::instance()->styles.separatorInView());
searchList()->addChild(group);
}

View File

@ -18,6 +18,7 @@
#include "app/context_access.h"
#include "app/modules/gui.h"
#include "app/transaction.h"
#include "app/ui/separator_in_view.h"
#include "app/ui/timeline/timeline.h"
#include "app/ui/user_data_popup.h"
#include "app/ui_context.h"
@ -54,6 +55,18 @@ class LayerPropertiesWindow : public app::gen::LayerProperties
, public doc::ContextObserver
, public doc::DocumentObserver {
public:
class BlendModeItem : public ListItem {
public:
BlendModeItem(const std::string& name,
const doc::BlendMode mode)
: ListItem(name)
, m_mode(mode) {
}
doc::BlendMode mode() const { return m_mode; }
private:
doc::BlendMode m_mode;
};
LayerPropertiesWindow()
: m_timer(250, this)
, m_document(nullptr)
@ -62,22 +75,30 @@ public:
name()->setMinSize(gfx::Size(128, 0));
name()->setExpansive(true);
mode()->addItem("Normal");
mode()->addItem("Multiply");
mode()->addItem("Screen");
mode()->addItem("Overlay");
mode()->addItem("Darken");
mode()->addItem("Lighten");
mode()->addItem("Color Dodge");
mode()->addItem("Color Burn");
mode()->addItem("Hard Light");
mode()->addItem("Soft Light");
mode()->addItem("Difference");
mode()->addItem("Exclusion");
mode()->addItem("Hue");
mode()->addItem("Saturation");
mode()->addItem("Color");
mode()->addItem("Luminosity");
mode()->addItem(new BlendModeItem("Normal", doc::BlendMode::NORMAL));
mode()->addItem(new SeparatorInView);
mode()->addItem(new BlendModeItem("Darken", doc::BlendMode::DARKEN));
mode()->addItem(new BlendModeItem("Multiply", doc::BlendMode::MULTIPLY));
mode()->addItem(new BlendModeItem("Color Burn", doc::BlendMode::COLOR_BURN));
mode()->addItem(new SeparatorInView);
mode()->addItem(new BlendModeItem("Lighten", doc::BlendMode::LIGHTEN));
mode()->addItem(new BlendModeItem("Screen", doc::BlendMode::SCREEN));
mode()->addItem(new BlendModeItem("Color Dodge", doc::BlendMode::COLOR_DODGE));
mode()->addItem(new BlendModeItem("Addition", doc::BlendMode::ADDITION));
mode()->addItem(new SeparatorInView);
mode()->addItem(new BlendModeItem("Overlay", doc::BlendMode::OVERLAY));
mode()->addItem(new BlendModeItem("Soft Light", doc::BlendMode::SOFT_LIGHT));
mode()->addItem(new BlendModeItem("Hard Light", doc::BlendMode::HARD_LIGHT));
mode()->addItem(new SeparatorInView);
mode()->addItem(new BlendModeItem("Difference", doc::BlendMode::DIFFERENCE));
mode()->addItem(new BlendModeItem("Exclusion", doc::BlendMode::EXCLUSION));
mode()->addItem(new BlendModeItem("Subtract", doc::BlendMode::SUBTRACT));
mode()->addItem(new BlendModeItem("Divide", doc::BlendMode::DIVIDE));
mode()->addItem(new SeparatorInView);
mode()->addItem(new BlendModeItem("Hue", doc::BlendMode::HSL_HUE));
mode()->addItem(new BlendModeItem("Saturation", doc::BlendMode::HSL_SATURATION));
mode()->addItem(new BlendModeItem("Color", doc::BlendMode::HSL_COLOR));
mode()->addItem(new BlendModeItem("Luminosity", doc::BlendMode::HSL_LUMINOSITY));
name()->Change.connect(base::Bind<void>(&LayerPropertiesWindow::onStartTimer, this));
mode()->Change.connect(base::Bind<void>(&LayerPropertiesWindow::onStartTimer, this));
@ -120,7 +141,11 @@ private:
}
BlendMode blendModeValue() const {
return (BlendMode)mode()->getSelectedItemIndex();
BlendModeItem* item = dynamic_cast<BlendModeItem*>(mode()->getSelectedItem());
if (item)
return item->mode();
else
return doc::BlendMode::NORMAL;
}
int opacityValue() const {
@ -290,7 +315,15 @@ private:
name()->setEnabled(true);
if (m_layer->isImage()) {
mode()->setSelectedItemIndex((int)static_cast<LayerImage*>(m_layer)->blendMode());
mode()->setSelectedItem(nullptr);
for (auto item : *mode()) {
if (auto blendModeItem = dynamic_cast<BlendModeItem*>(item)) {
if (blendModeItem->mode() == static_cast<LayerImage*>(m_layer)->blendMode()) {
mode()->setSelectedItem(item);
break;
}
}
}
mode()->setEnabled(!m_layer->isBackground());
opacity()->setValue(static_cast<LayerImage*>(m_layer)->opacity());
opacity()->setEnabled(!m_layer->isBackground());

View File

@ -20,6 +20,7 @@
#include "app/resource_finder.h"
#include "app/send_crash.h"
#include "app/ui/color_button.h"
#include "app/ui/separator_in_view.h"
#include "app/ui/skin/skin_theme.h"
#include "base/bind.h"
#include "base/convert_to.h"
@ -623,9 +624,8 @@ private:
if (first) {
first = false;
auto sep = new Separator(base::normalize_path(path), HORIZONTAL);
sep->setStyle(theme->styles.separatorInView());
themeList()->addChild(sep);
themeList()->addChild(
new SeparatorInView(base::normalize_path(path), HORIZONTAL));
}
ThemeItem* item = new ThemeItem(fullPath, fn);
@ -648,9 +648,8 @@ private:
if (first) {
first = false;
auto sep = new Separator("Extension Themes", HORIZONTAL);
sep->setStyle(theme->styles.separatorInView());
themeList()->addChild(sep);
themeList()->addChild(
new Separator("Extension Themes", HORIZONTAL));
}
for (auto it : ext->themes()) {

View File

@ -13,6 +13,7 @@
#include "app/resource_finder.h"
#include "app/ui/browser_view.h"
#include "app/ui/main_window.h"
#include "app/ui/separator_in_view.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui/workspace.h"
#include "base/file_handle.h"
@ -427,8 +428,7 @@ private:
}
void addSeparator() {
auto sep = new Separator("", HORIZONTAL);
sep->setStyle(SkinTheme::instance()->styles.separatorInView());
auto sep = new SeparatorInView(std::string(), HORIZONTAL);
sep->setBorder(gfx::Border(0, font()->height(), 0, font()->height()));
sep->setExpansive(true);
addChild(sep);

View File

@ -15,6 +15,7 @@
#include "app/crash/session.h"
#include "app/modules/gui.h"
#include "app/ui/drop_down_button.h"
#include "app/ui/separator_in_view.h"
#include "app/ui/skin/skin_theme.h"
#include "app/ui/workspace.h"
#include "base/bind.h"
@ -114,8 +115,7 @@ void DataRecoveryView::fillList()
if (session->isEmpty())
continue;
auto sep = new Separator(session->name(), HORIZONTAL);
sep->setStyle(SkinTheme::instance()->styles.separatorInView());
auto sep = new SeparatorInView(session->name(), HORIZONTAL);
sep->setBorder(sep->border() + gfx::Border(0, 8, 0, 8)*guiscale());
m_listBox.addChild(sep);

View File

@ -263,11 +263,12 @@ public:
std::string extrasLabel() const {
std::string label = "Resize: " + m_extras->resize()->getSelectedItem()->text();
auto layerItem = m_extras->layers()->getSelectedItem();
if (layerItem && !layerItem->getValue().empty())
auto layerItem = dynamic_cast<ListItem*>(m_extras->layers()->getSelectedItem());
if (layerItem &&
!layerItem->getValue().empty())
label += ", " + layerItem->text();
auto frameItem = m_extras->frames()->getSelectedItem();
auto frameItem = dynamic_cast<ListItem*>(m_extras->frames()->getSelectedItem());
if (frameItem && !frameItem->getValue().empty())
label += ", " + frameItem->text();
@ -871,7 +872,8 @@ void FileSelector::onFileTypeChange()
if (m_type == FileSelectorType::Open ||
m_type == FileSelectorType::OpenMultiple) {
std::string origShowExtensions = fileType()->getItem(0)->getValue();
std::string origShowExtensions =
dynamic_cast<ListItem*>(fileType()->getItem(0))->getValue();
preferred_open_extensions[origShowExtensions] = fileType()->getValue();
}
}

View File

@ -1,5 +1,5 @@
// Aseprite
// Copyright (C) 2016 David Capello
// Copyright (C) 2016-2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
@ -58,10 +58,10 @@ FrameListItem::FrameListItem(doc::FrameTag* tag)
void fill_layers_combobox(const doc::Sprite* sprite, ui::ComboBox* layers, const std::string& defLayer)
{
int i = layers->addItem("Visible layers");
layers->getItem(i)->setValue(kAllLayers);
dynamic_cast<LayerListItem*>(layers->getItem(i))->setValue(kAllLayers);
i = layers->addItem("Selected layers");
layers->getItem(i)->setValue(kSelectedLayers);
dynamic_cast<LayerListItem*>(layers->getItem(i))->setValue(kSelectedLayers);
if (defLayer == kSelectedLayers)
layers->setSelectedItemIndex(i);
@ -77,10 +77,10 @@ void fill_layers_combobox(const doc::Sprite* sprite, ui::ComboBox* layers, const
void fill_frames_combobox(const doc::Sprite* sprite, ui::ComboBox* frames, const std::string& defFrame)
{
int i = frames->addItem("All frames");
frames->getItem(i)->setValue(kAllFrames);
dynamic_cast<FrameListItem*>(frames->getItem(i))->setValue(kAllFrames);
i = frames->addItem("Selected frames");
frames->getItem(i)->setValue(kSelectedFrames);
dynamic_cast<FrameListItem*>(frames->getItem(i))->setValue(kSelectedFrames);
if (defFrame == kSelectedFrames)
frames->setSelectedItemIndex(i);

View File

@ -0,0 +1,29 @@
// Aseprite
// Copyright (C) 2017 David Capello
//
// This program is distributed under the terms of
// the End-User License Agreement for Aseprite.
#ifndef APP_UI_SEPARATOR_IN_VIEW_H_INCLUDED
#define APP_UI_SEPARATOR_IN_VIEW_H_INCLUDED
#pragma once
#include "app/ui/skin/skin_theme.h"
#include "ui/separator.h"
namespace app {
class SeparatorInView : public ui::Separator {
public:
SeparatorInView(const std::string& text = std::string(),
int align = ui::HORIZONTAL)
: Separator(text, align) {
setStyle(skin::SkinTheme::instance()->styles.separatorInView());
if (text.empty())
setBorder(border() + gfx::Border(0, 2, 0, 2)*ui::guiscale());
}
};
} // namespace app
#endif

View File

@ -37,6 +37,16 @@ namespace {
#define blend_difference(b, s) (ABS((b) - (s)))
#define blend_exclusion(b, s, t) ((t) = MUL_UN8((b), (s), (t)), ((b) + (s) - 2*(t)))
inline uint32_t blend_divide(uint32_t b, uint32_t s)
{
if (b == 0)
return 0;
else if (b >= s)
return 255;
else
return DIV_UN8(b, s); // return b / s
}
inline uint32_t blend_color_dodge(uint32_t b, uint32_t s)
{
if (b == 0)
@ -432,6 +442,33 @@ color_t rgba_blender_hsl_luminosity(color_t backdrop, color_t src, int opacity)
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_addition(color_t backdrop, color_t src, int opacity)
{
int r = rgba_getr(backdrop) + rgba_getr(src);
int g = rgba_getg(backdrop) + rgba_getg(src);
int b = rgba_getb(backdrop) + rgba_getb(src);
src = rgba(MIN(r, 255), MIN(g, 255), MIN(b, 255), 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_subtract(color_t backdrop, color_t src, int opacity)
{
int r = rgba_getr(backdrop) - rgba_getr(src);
int g = rgba_getg(backdrop) - rgba_getg(src);
int b = rgba_getb(backdrop) - rgba_getb(src);
src = rgba(MAX(r, 0), MAX(g, 0), MAX(b, 0), 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
color_t rgba_blender_divide(color_t backdrop, color_t src, int opacity)
{
int r = blend_divide(rgba_getr(backdrop), rgba_getr(src));
int g = blend_divide(rgba_getg(backdrop), rgba_getg(src));
int b = blend_divide(rgba_getb(backdrop), rgba_getb(src));
src = rgba(r, g, b, 0) | (src & rgba_a_mask);
return rgba_blender_normal(backdrop, src, opacity);
}
//////////////////////////////////////////////////////////////////////
// GRAY blenders
@ -591,6 +628,27 @@ color_t graya_blender_exclusion(color_t backdrop, color_t src, int opacity)
return graya_blender_normal(backdrop, src, opacity);
}
color_t graya_blender_addition(color_t backdrop, color_t src, int opacity)
{
int v = graya_getv(backdrop) + graya_getv(src);
src = graya(MIN(v, 255), 0) | (src & graya_a_mask);
return graya_blender_normal(backdrop, src, opacity);
}
color_t graya_blender_subtract(color_t backdrop, color_t src, int opacity)
{
int v = graya_getv(backdrop) - graya_getv(src);
src = graya(MAX(v, 0), 0) | (src & graya_a_mask);
return graya_blender_normal(backdrop, src, opacity);
}
color_t graya_blender_divide(color_t backdrop, color_t src, int opacity)
{
int v = blend_divide(graya_getv(backdrop), graya_getv(src));
src = graya(v, 0) | (src & graya_a_mask);
return graya_blender_normal(backdrop, src, opacity);
}
//////////////////////////////////////////////////////////////////////
// indexed
@ -627,6 +685,9 @@ BlendFunc get_rgba_blender(BlendMode blendmode)
case BlendMode::HSL_SATURATION: return rgba_blender_hsl_saturation;
case BlendMode::HSL_COLOR: return rgba_blender_hsl_color;
case BlendMode::HSL_LUMINOSITY: return rgba_blender_hsl_luminosity;
case BlendMode::ADDITION: return rgba_blender_addition;
case BlendMode::SUBTRACT: return rgba_blender_subtract;
case BlendMode::DIVIDE: return rgba_blender_divide;
}
ASSERT(false);
return rgba_blender_src;
@ -657,6 +718,9 @@ BlendFunc get_graya_blender(BlendMode blendmode)
case BlendMode::HSL_SATURATION: return graya_blender_normal;
case BlendMode::HSL_COLOR: return graya_blender_normal;
case BlendMode::HSL_LUMINOSITY: return graya_blender_normal;
case BlendMode::ADDITION: return graya_blender_addition;
case BlendMode::SUBTRACT: return graya_blender_subtract;
case BlendMode::DIVIDE: return graya_blender_divide;
}
ASSERT(false);
return graya_blender_src;

View File

@ -36,6 +36,9 @@ namespace doc {
color_t rgba_blender_hsl_saturation(color_t backdrop, color_t src, int opacity);
color_t rgba_blender_hsl_color(color_t backdrop, color_t src, int opacity);
color_t rgba_blender_hsl_luminosity(color_t backdrop, color_t src, int opacity);
color_t rgba_blender_addition(color_t backdrop, color_t src, int opacity);
color_t rgba_blender_subtract(color_t backdrop, color_t src, int opacity);
color_t rgba_blender_divide(color_t backdrop, color_t src, int opacity);
color_t graya_blender_src(color_t backdrop, color_t src, int opacity);
color_t graya_blender_merge(color_t backdrop, color_t src, int opacity);

View File

@ -1,5 +1,5 @@
// Aseprite Document Library
// Copyright (c) 2001-2015 David Capello
// Copyright (c) 2001-2017 David Capello
//
// This file is released under the terms of the MIT license.
// Read LICENSE.txt for more information.
@ -37,7 +37,10 @@ namespace doc {
HSL_HUE = 12,
HSL_SATURATION = 13,
HSL_COLOR = 14,
HSL_LUMINOSITY = 15
HSL_LUMINOSITY = 15,
ADDITION = 16,
SUBTRACT = 17,
DIVIDE = 18
};
std::string blend_mode_to_string(BlendMode blendMode);

View File

@ -146,7 +146,7 @@ void ComboBox::setUseCustomWidget(bool state)
m_useCustomWidget = state;
}
int ComboBox::addItem(ListItem* item)
int ComboBox::addItem(Widget* item)
{
bool sel_first = m_items.empty();
@ -163,7 +163,7 @@ int ComboBox::addItem(const std::string& text)
return addItem(new ListItem(text));
}
void ComboBox::insertItem(int itemIndex, ListItem* item)
void ComboBox::insertItem(int itemIndex, Widget* item)
{
bool sel_first = m_items.empty();
@ -178,12 +178,10 @@ void ComboBox::insertItem(int itemIndex, const std::string& text)
insertItem(itemIndex, new ListItem(text));
}
void ComboBox::removeItem(ListItem* item)
void ComboBox::removeItem(Widget* item)
{
ListItems::iterator it = std::find(m_items.begin(), m_items.end(), item);
auto it = std::find(m_items.begin(), m_items.end(), item);
ASSERT(it != m_items.end());
if (it != m_items.end())
m_items.erase(it);
@ -194,7 +192,7 @@ void ComboBox::removeItem(int itemIndex)
{
ASSERT(itemIndex >= 0 && (std::size_t)itemIndex < m_items.size());
ListItem* item = m_items[itemIndex];
Widget* item = m_items[itemIndex];
m_items.erase(m_items.begin() + itemIndex);
delete item;
@ -202,9 +200,8 @@ void ComboBox::removeItem(int itemIndex)
void ComboBox::removeAllItems()
{
ListItems::iterator it, end = m_items.end();
for (it = m_items.begin(); it != end; ++it)
delete *it;
for (Widget* item : m_items)
delete item; // widget
m_items.clear();
m_selected = -1;
@ -215,7 +212,7 @@ int ComboBox::getItemCount() const
return m_items.size();
}
ListItem* ComboBox::getItem(int itemIndex)
Widget* ComboBox::getItem(int itemIndex)
{
if (itemIndex >= 0 && (std::size_t)itemIndex < m_items.size()) {
return m_items[itemIndex];
@ -227,7 +224,7 @@ ListItem* ComboBox::getItem(int itemIndex)
const std::string& ComboBox::getItemText(int itemIndex) const
{
if (itemIndex >= 0 && (std::size_t)itemIndex < m_items.size()) {
ListItem* item = m_items[itemIndex];
Widget* item = m_items[itemIndex];
return item->text();
}
else {
@ -241,14 +238,14 @@ void ComboBox::setItemText(int itemIndex, const std::string& text)
{
ASSERT(itemIndex >= 0 && (std::size_t)itemIndex < m_items.size());
ListItem* item = m_items[itemIndex];
Widget* item = m_items[itemIndex];
item->setText(text);
}
int ComboBox::findItemIndex(const std::string& text) const
{
int i = 0;
for (const ListItem* item : m_items) {
for (const Widget* item : m_items) {
if ((m_casesensitive && item->text() == text) ||
(!m_casesensitive && item->text() == text)) {
return i;
@ -261,27 +258,30 @@ int ComboBox::findItemIndex(const std::string& text) const
int ComboBox::findItemIndexByValue(const std::string& value) const
{
int i = 0;
for (const ListItem* item : m_items) {
if (item->getValue() == value)
return i;
i++;
for (const Widget* item : m_items) {
if (auto listItem = dynamic_cast<const ListItem*>(item)) {
if (listItem->getValue() == value)
return i;
}
++i;
}
return -1;
}
ListItem* ComboBox::getSelectedItem() const
Widget* ComboBox::getSelectedItem() const
{
return (!m_items.empty() ? m_items[m_selected]: NULL);
}
void ComboBox::setSelectedItem(ListItem* item)
void ComboBox::setSelectedItem(Widget* item)
{
ListItems::iterator it = std::find(m_items.begin(), m_items.end(), item);
ASSERT(it != m_items.end());
auto it = std::find(m_items.begin(), m_items.end(), item);
if (it != m_items.end())
setSelectedItemIndex(std::distance(m_items.begin(), it));
else if (m_selected >= 0) {
m_selected = -1;
onChange();
}
}
int ComboBox::getSelectedItemIndex() const
@ -296,8 +296,8 @@ void ComboBox::setSelectedItemIndex(int itemIndex)
m_selected != itemIndex) {
m_selected = itemIndex;
ListItems::iterator it = m_items.begin() + itemIndex;
ListItem* item = *it;
auto it = m_items.begin() + itemIndex;
Widget* item = *it;
m_entry->setText(item->text());
if (isEditable())
m_entry->setCaretToEnd();
@ -310,13 +310,12 @@ std::string ComboBox::getValue() const
{
if (isEditable())
return m_entry->text();
else {
int index = getSelectedItemIndex();
if (index >= 0)
return m_items[index]->getValue();
else
return std::string();
int index = getSelectedItemIndex();
if (index >= 0) {
if (auto listItem = dynamic_cast<ListItem*>(m_items[index]))
return listItem->getValue();
}
return std::string();
}
void ComboBox::setValue(const std::string& value)
@ -419,8 +418,8 @@ void ComboBox::onSizeHint(SizeHintEvent& ev)
Size reqSize = entrySize;
// Get the text-length of every item
ListItems::iterator it, end = m_items.end();
for (it = m_items.begin(); it != end; ++it) {
auto end = m_items.end();
for (auto it = m_items.begin(); it != end; ++it) {
int item_w =
2*guiscale()+
font()->textLength((*it)->text().c_str())+
@ -603,7 +602,7 @@ void ComboBox::openListBox()
gfx::Size size;
size.w = m_button->bounds().x2() - entryBounds.x - view->border().width();
size.h = viewport->border().height();
for (ListItem* item : m_items)
for (Widget* item : m_items)
size.h += item->sizeHint().h;
int max = MAX(entryBounds.y, ui::display_h() - entryBounds.y2()) - 8*guiscale();
@ -710,7 +709,7 @@ void ComboBox::putSelectedItemAsCustomWidget()
if (!useCustomWidget())
return;
ListItem* item = getSelectedItem();
Widget* item = getSelectedItem();
if (item && item->parent() == nullptr) {
if (!m_listbox) {
item->setBounds(m_entry->childrenBounds());

View File

@ -20,7 +20,6 @@ namespace ui {
class Entry;
class Event;
class ListBox;
class ListItem;
class Window;
class ComboBoxEntry;
@ -31,13 +30,13 @@ namespace ui {
friend class ComboBoxListBox;
public:
typedef std::vector<ListItem*> ListItems;
typedef std::vector<Widget*> Items;
ComboBox();
~ComboBox();
ListItems::iterator begin() { return m_items.begin(); }
ListItems::iterator end() { return m_items.end(); }
Items::iterator begin() { return m_items.begin(); }
Items::iterator end() { return m_items.end(); }
void setEditable(bool state);
void setClickOpen(bool state);
@ -49,13 +48,13 @@ namespace ui {
bool isCaseSensitive() const { return m_casesensitive; }
bool useCustomWidget() const { return m_useCustomWidget; }
int addItem(ListItem* item);
int addItem(Widget* item);
int addItem(const std::string& text);
void insertItem(int itemIndex, ListItem* item);
void insertItem(int itemIndex, Widget* item);
void insertItem(int itemIndex, const std::string& text);
// Removes the given item (you must delete it).
void removeItem(ListItem* item);
void removeItem(Widget* item);
// Removes and deletes the given item.
void removeItem(int itemIndex);
@ -64,14 +63,14 @@ namespace ui {
int getItemCount() const;
ListItem* getItem(int itemIndex);
Widget* getItem(int itemIndex);
const std::string& getItemText(int itemIndex) const;
void setItemText(int itemIndex, const std::string& text);
int findItemIndex(const std::string& text) const;
int findItemIndexByValue(const std::string& value) const;
ListItem* getSelectedItem() const;
void setSelectedItem(ListItem* item);
Widget* getSelectedItem() const;
void setSelectedItem(Widget* item);
int getSelectedItemIndex() const;
void setSelectedItemIndex(int itemIndex);
@ -110,7 +109,7 @@ namespace ui {
Button* m_button;
Window* m_window;
ComboBoxListBox* m_listbox;
ListItems m_items;
Items m_items;
int m_selected;
bool m_editable;
bool m_clickopen;

View File

@ -13,8 +13,9 @@
#include "base/fs.h"
#include "ui/listitem.h"
#include "ui/message.h"
#include "ui/size_hint_event.h"
#include "ui/resize_event.h"
#include "ui/separator.h"
#include "ui/size_hint_event.h"
#include "ui/system.h"
#include "ui/theme.h"
#include "ui/view.h"
@ -437,7 +438,11 @@ int ListBox::advanceIndexThroughVisibleItems(
}
else {
Widget* item = getChildByIndex(index);
if (item && !item->hasFlags(HIDDEN)) {
if (item &&
!item->hasFlags(HIDDEN) &&
// We can completelly ignore separators from navigation
// keys.
!dynamic_cast<Separator*>(item)) {
lastVisibleIndex = index;
delta -= sgn;
}