#include "slider.h" #include "main_window.h" #include #include #include #include #include #include #include "utils.h" #include #include "icons.h" #define CMD_SLIDER_MOVED 0x90 #define CMD_TEXT_CHANGED 0x91 #define CMD_SLIDER_CHANGED 0x92 #define CMD_CHANGE_MODE 0x93 using namespace BPrivate; void LooperSlider::UpdateLogScaler() { scaler->update_min_max(min, max); } void LooperSlider::UpdateSlider(bool update_value) { if (recreate_slider) { if (slider != NULL) { if (!text_edit_mode) text_layout->RemoveView(slider); delete slider; } int32 min, max; if (logarithmic) { scaler->update_min_max(this->min, this->max); } min = std::floor(this->min / tick); max = std::ceil(this->max / tick); slider = new BSlider(NULL, NULL, make_self_msg(CMD_SLIDER_CHANGED), min, max, B_HORIZONTAL); slider->SetModificationMessage(make_self_msg(CMD_SLIDER_MOVED)); BSize slider_min = slider->MinSize(); BSize text_min = text->MinSize(); BSize min_combined = BSize(std::max(slider_min.Width(), text_min.Width()), std::max(slider_min.Height(), text_min.Height())); slider->SetExplicitMinSize(min_combined); text->SetExplicitMinSize(min_combined); slider->SetTarget(this); if (!text_edit_mode) { auto item = text_layout->AddView(1, slider); item->SetExplicitAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER)); } } if (update_value || recreate_slider) slider->SetValue((logarithmic ? scaler->scale_log(this->value) : this->value) / tick); // slider->SetLimitLabels(min_label == NULL ? "" : min_label, max_label == NULL ? "" : max_label); min_label_view->SetText(min_label == NULL ? "" : min_label); max_label_view->SetText(max_label == NULL ? "" : max_label); if (min_label != NULL || max_label != NULL) { limits_view->Show(); } else { limits_view->Hide(); } recreate_slider = false; } void LooperSlider::set_min(double min) { this->min = min; recreate_slider = true; } void LooperSlider::set_max(double max) { this->max = max; recreate_slider = true; } void LooperSlider::SetMinLabel(const char *label) { if (min_label != NULL) free((void*)min_label); min_label = label == NULL ? NULL : strdup(label); UpdateSlider(); } void LooperSlider::SetMaxLabel(const char *label) { if (max_label != NULL) free((void*)max_label); max_label = label == NULL ? NULL : strdup(label); UpdateSlider(); } void LooperSlider::set_tick(double value) { this->tick = value; recreate_slider = true; } const char *LooperSlider::MinLabel() { return min_label; } const char *LooperSlider::MaxLabel() { return max_label; } void LooperSlider::SetLimitLabels(const char *min, const char *max) { SetMinLabel(min); SetMaxLabel(max); } LooperSlider::~LooperSlider() { if (min_label != NULL) free((void*)min_label); if (max_label != NULL) free((void*)max_label); delete slider; delete btn; delete text; delete scaler; delete text_mode_bitmap; delete slider_mode_bitmap; } void LooperSlider::SetMin(double min) { set_min(min); UpdateSlider(); } void LooperSlider::SetMax(double max) { set_max(max); UpdateSlider(); } double LooperSlider::Min() { return min; } double LooperSlider::Max() { return max; } void LooperSlider::SetLimits(double min, double max) { set_min(min); set_max(max); UpdateSlider(); } void LooperSlider::SetTick(double value) { set_tick(value); UpdateSlider(); } double LooperSlider::Tick() { return tick; } void LooperSlider::SetLogarithmic(bool logarithmic) { this->logarithmic = logarithmic; recreate_slider = true; UpdateSlider(true); } bool LooperSlider::IsLogarithmic() { return this->logarithmic; } void LooperSlider::MessageReceived(BMessage *msg) { switch (msg->what) { case CMD_SLIDER_MOVED: case CMD_SLIDER_CHANGED: { this->pressed = msg->what == CMD_SLIDER_MOVED; if (logarithmic) { SetValueDouble(scaler->unscale_log(((double)msg->GetInt32("be:value", 0) * tick))); } else { SetValueDouble(((double)msg->GetInt32("be:value", 0)) * tick); } SendChangeMsg(); } break; case CMD_TEXT_CHANGED: { bool text_valid = true; double new_value; try { new_value = std::stod(text->Text()); } catch(std::out_of_range) { text_valid = false; } catch (std::invalid_argument) { text_valid = false; } if (text_valid) { SetValueDouble(new_value); SendChangeMsg(); } } break; case CMD_CHANGE_MODE: { text_edit_mode = !text_edit_mode; { auto update_label_msg = new BMessage(CMD_UPDATE_LABEL_SETTING); MessageReceived(update_label_msg); delete update_label_msg; } if (text_edit_mode) { pressed = true; text->SetText(fmt::to_string(ValueDouble()).c_str()); text_layout->RemoveView(slider); auto item = text_layout->AddView(1, text); item->SetExplicitAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER)); } else { pressed = false; text_layout->RemoveView(text); auto item = text_layout->AddView(1, slider); item->SetExplicitAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER)); } } break; case CMD_UPDATE_LABEL_SETTING: { const char *precision_text[2] = {"Imprecise", "Precise"}; BBitmap *precision_bitmap[2] = {LooperSlider::slider_mode_bitmap, LooperSlider::text_mode_bitmap}; BBitmap *bitmap = precision_bitmap[text_edit_mode ? 0 : 1]; if (show_labels || bitmap == NULL) { btn->SetLabel(precision_text[text_edit_mode ? 0 : 1]); } else { btn->SetLabel(""); } if (show_icons && bitmap != NULL) { btn->SetIcon(bitmap); } else { btn->SetIcon(get_empty_icon()); } } break; } } void LooperSlider::SendChangeMsg() { BMessage *msg = change_msg; msg->SetDouble("be:value", this->value); msg->SetBool("catmeow:pressed", pressed); Invoke(msg); } void LooperSlider::SetValue(int32 value) { SetValueDouble(value * tick); } void LooperSlider::SetValueDouble(double value) { this->value = value; BControl::SetValue(std::round(value / tick)); UpdateSlider(true); } double LooperSlider::ValueDouble() { return this->value; } void LooperSlider::SetLabel(const char *label) { this->label = label; text_label->SetText(label); } void LooperSlider::set_logarithmic(bool logarithmic) { this->logarithmic = logarithmic; recreate_slider = true; } void LooperSlider::SetValueChangedMsg(BMessage *msg) { this->change_msg = msg; } LooperSlider::LooperSlider(const char *name, const char *label, BMessage *msg, uint32_t flags, double min, double max, double tick, bool logarithmic) : BControl(name, label, msg, flags) { scaler = new LooperLogScaler(min, max); text_mode_bitmap = load_icon(ICON_EDIT_TEXT); slider_mode_bitmap = load_icon(ICON_EDIT_SLIDER); auto *group_layout = new BGroupLayout(B_HORIZONTAL); SetLayout(group_layout); group_layout->SetSpacing(0.0); text_layout_view = new BGroupView(B_VERTICAL); text_layout = text_layout_view->GroupLayout(); text_layout->SetInsets(0, 0, 0, 0); text_layout->SetSpacing(0.0); text_label = new BStringView(NULL, ""); text = new BTextControl(NULL, "", make_self_msg(CMD_TEXT_CHANGED)); text->ResizeToPreferred(); text->SetModificationMessage(make_self_msg(CMD_TEXT_CHANGED)); text_layout->AddView(text_label); limits_view = new BGroupView(B_HORIZONTAL); auto limits_layout = limits_view->GroupLayout(); min_label_view = new BStringView(NULL, ""); max_label_view = new BStringView(NULL, ""); text_layout->AddView(limits_view); auto min_label_item = limits_layout->AddView(min_label_view); min_label_item->SetExplicitAlignment(BAlignment(B_ALIGN_LEFT, B_ALIGN_TOP)); auto max_label_item = limits_layout->AddView(max_label_view); max_label_item->SetExplicitAlignment(BAlignment(B_ALIGN_RIGHT, B_ALIGN_TOP)); limits_view->Hide(); text->SetTarget(this); btn = new BButton("", make_self_msg(CMD_CHANGE_MODE)); btn->SetTarget(this); group_layout->AddView(0, text_layout_view); group_layout->AddView(1, btn); SetValueChangedMsg(msg); set_min(min); set_max(max); set_tick(tick); set_logarithmic(logarithmic); UpdateSlider(true); SetValueDouble(min); SetLabel(label); InvalidateLayout(true); } void LooperSlider::AttachedToWindow() { { BMessage *msg = make_self_msg(CMD_CHANGE_MODE); MessageReceived(msg); MessageReceived(msg); delete msg; } }