10 #include <QMouseEvent>
20 const QColor focus_indicator = Qt::gray;
22 const QColor handle_background = Qt::lightGray;
24 const QColor handle_indicator = Qt::gray;
26 const QColor handle_hover = Qt::darkGray;
28 const QColor handle_dragged = handle_hover;
33 const double focus_bar_gap = 3;
35 const double focus_bar_height = 1;
37 const double handle_bar_gap = 2;
39 const double handle_bar_width = 4;
41 const double handle_gap = 1;
43 const double handle_radius = 8;
45 const double handle_indicator_radius = 4;
47 const double handle_active_indicator_radius = handle_indicator_radius + 1;
49 const double extra_height = std::max(focus_bar_gap + focus_bar_height,
50 handle_gap + handle_radius * 2);
108 setFocusPolicy(Qt::StrongFocus);
109 setMouseTracking(
true);
110 setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed,
111 QSizePolicy::Slider));
126 qWarning() <<
"Inconsistent icon sizes:" << icon.size() <<
"and"
193 return QSize(
total() * icon_size.width() + 2 * metrics::handle_radius,
194 icon_size.height() + metrics::extra_height);
210 return QSize(
total() * 5 + 2 * metrics::handle_radius,
211 icon_size.height() + metrics::extra_height);
221 if (
event->type() == QEvent::KeyPress) {
222 auto kevt =
dynamic_cast<QKeyEvent *
>(
event);
224 if (kevt->key() == Qt::Key_Tab &&
move_focus(
true)) {
227 }
else if (kevt->key() == Qt::Key_Backtab &&
move_focus(
false)) {
242 if (
event->reason() == Qt::BacktabFocusReason) {
244 }
else if (
event->reason() == Qt::TabFocusReason) {
251 QWidget::focusInEvent(
event);
264 if (
event->modifiers() == Qt::NoModifier) {
265 switch (
event->key()) {
296 QWidget::keyPressEvent(
event);
306 QWidget::leaveEvent(
event);
314 if (
event->buttons() == Qt::LeftButton
315 &&
event->modifiers() == Qt::NoModifier) {
319 QWidget::mouseMoveEvent(
event);
328 &&
event->modifiers() == Qt::NoModifier) {
340 QWidget::mouseMoveEvent(
event);
348 if (
event->buttons() == Qt::LeftButton
349 &&
event->modifiers() == Qt::NoModifier) {
353 QWidget::mousePressEvent(
event);
363 QWidget::mouseReleaseEvent(
event);
376 const auto iheight =
m_categories.front().icon.height();
380 p.translate(
m_geom.left_margin, 0);
384 p.setRenderHint(QPainter::Antialiasing);
385 double xmin = 0, xmax = 0;
386 for (std::size_t i = 0; i <
m_values.size(); ++i) {
388 p.drawTiledPixmap(QRectF(xmin, 0, xmax - xmin, iheight),
393 p.setBrush(colors::focus_indicator);
394 p.drawRect(QRectF(xmin, iheight + metrics::focus_bar_gap, xmax - xmin,
395 metrics::focus_bar_height));
402 for (
auto h : handles) {
403 auto x =
m_geom.item_width * h.location;
406 p.setBrush(colors::handle_background);
407 p.drawRect(QRectF(x - metrics::handle_bar_width / 2,
408 metrics::handle_bar_gap, metrics::handle_bar_width,
409 iheight + metrics::handle_gap));
410 p.drawEllipse(QPointF(x, iheight + metrics::handle_gap
411 + metrics::handle_radius - 1),
412 metrics::handle_radius, metrics::handle_radius);
417 double inner_radius = is_dragged
418 ? metrics::handle_active_indicator_radius
419 : metrics::handle_indicator_radius;
420 p.setBrush(is_dragged ? colors::handle_dragged
421 : is_closest ? colors::handle_hover
422 : colors::handle_indicator);
423 p.drawEllipse(QPointF(x, iheight + metrics::handle_gap
424 + metrics::handle_radius - 1),
425 inner_radius, inner_radius);
480 for (
int i = taker - 1; i >= 0; --i) {
516 int step = forward ? 1 : -1;
535 const auto handle_x = [
this](
const handle &h) {
536 return m_geom.left_margin + h.location *
m_geom.item_width;
538 const auto best_handle =
539 std::min_element(handles.begin(), handles.end(),
541 return std::abs(where.x() - handle_x(a))
542 < std::abs(where.x() - handle_x(b));
544 return best_handle->index;
557 std::round((where.x() -
m_geom.left_margin) /
m_geom.item_width);
564 bool moving_left = current > target;
570 for (
int i = 0; i < std::abs(current - target); ++i) {
572 if (!
grab_item(taker, 1, moving_left, !moving_left)) {
586 const auto icon_size =
m_categories.front().icon.size();
587 const auto items =
total();
595 m_geom.icons_width = items * icon_size.width();
596 int total_width =
m_geom.icons_width + 2 * metrics::handle_radius;
599 if (total_width > width()) {
600 int available_width = width() - 2 * metrics::handle_radius;
602 available_width / icon_size.width();
603 m_geom.icons_width = icon_count * icon_size.width();
606 m_geom.left_margin = (width() -
m_geom.icons_width) / 2;
609 m_geom.item_width =
static_cast<double>(
m_geom.icons_width) / items;
617 std::vector<handle> handles;
620 for (
int i = 0; i <
m_values.size() - 1; ++i) {
623 handles.push_back({i, location});
std::vector< int > m_values
Number of items in each category.
void mouseMoveEvent(QMouseEvent *event) override
Moves the current handle when dragging the mouse.
void paintEvent(QPaintEvent *event) override
Draws the widget.
void resizeEvent(QResizeEvent *event) override
Updates cached geometry information.
int m_closest_handle
Index of the handle being dragged with the mouse.
void set_range(std::size_t category, int min, int max)
Sets the minimum and maximum number of items a category can have.
void focus_some_category()
Makes sure the focused category is a visible one.
std::vector< int > values() const
Retrieves the number of items in each category.
std::vector< category > m_categories
Category data.
struct freeciv::multi_slider::@154 m_geom
Cached geometry information.
void mouseDoubleClickEvent(QMouseEvent *event) override
Moves the closest handle when double-clicking.
bool move_focus(bool forward)
Moves focus to the next or previous visible category.
multi_slider(QWidget *parent=nullptr)
Constructor.
int handle_near(const QPoint &where)
Finds the index of the handle closest to the given position.
void set_values(const std::vector< int > &values)
Sets the contents of all item categories.
void mousePressEvent(QMouseEvent *event) override
Sets the current handle when pressing a mouse button.
bool event(QEvent *event) override
Overrides tab handling to also cycle through visible categories.
void leaveEvent(QEvent *event) override
Sopts highlighting the closest handle.
bool grab_item(std::size_t taker, int amount, bool from_left=true, bool from_right=true)
Grab an item from elsewhere and adds it to the taker category.
void mouseReleaseEvent(QMouseEvent *event) override
Unsets the current handle when releasing a mouse button.
void update_cached_geometry()
Updates cached geometry information.
std::size_t total() const
Returns the total number of items controlled by this widget.
void keyPressEvent(QKeyEvent *event) override
Handles arrow keys: left/right to change the focused category, up/down to add or remove items.
bool move_handle(int handle, const QPoint &where)
Tries to move a handle closer to a given position.
std::vector< handle > visible_handles() const
Returns the list of all visible handles.
int m_focused_category
Index of the category receiving keyboard input.
std::size_t add_category(const QPixmap &icon)
Adds a category.
void values_changed(const std::vector< int > &values) const
QSize minimumSizeHint() const override
Minimum size of the widget.
QSize sizeHint() const override
Preferred size of the widget.
void focusInEvent(QFocusEvent *event) override
Focuses the first or last category when focus is gained with the keyboard.
void exchange(std::size_t giver, std::size_t taker, int amount)
Exchange items between two categories.
#define fc_assert_ret(condition)
#define fc_assert_ret_val(condition, val)
Constants used when drawing the button.
bool allowed(int value) const
Checks if the category could take some value.